@vxnus/siduri 0.1.11 → 2.0.0
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/builtin-manifests.js +15 -30
- package/dist/clean-machine-e2e.test.js +13 -89
- package/dist/configurators.test.js +1 -1
- package/dist/db.d.ts +0 -12
- package/dist/db.js +7 -144
- package/dist/discovery.js +5 -7
- package/dist/discovery.test.js +5 -4
- package/dist/doctor-db.test.js +2 -91
- package/dist/doctor.js +8 -40
- package/dist/generator.test.js +1 -1
- package/dist/release-check.js +4 -8
- package/package.json +2 -4
|
@@ -5,7 +5,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
5
5
|
{
|
|
6
6
|
name: '@siduri-x/behavior',
|
|
7
7
|
organType: 'behavior',
|
|
8
|
-
version: '
|
|
8
|
+
version: '2.0.0',
|
|
9
9
|
displayName: 'Behavior (Active Self Directives)',
|
|
10
10
|
description: 'Atomic directive state machine and personality projection compiler',
|
|
11
11
|
entrypoint: './dist/index.js',
|
|
@@ -32,7 +32,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
32
32
|
{
|
|
33
33
|
name: '@siduri-x/body',
|
|
34
34
|
organType: 'body',
|
|
35
|
-
version: '
|
|
35
|
+
version: '2.0.0',
|
|
36
36
|
displayName: 'Body (Live2D & Avatar State)',
|
|
37
37
|
description: 'Renderer-agnostic avatar expression and embodiment event adapter',
|
|
38
38
|
entrypoint: './dist/index.js',
|
|
@@ -66,7 +66,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
66
66
|
{
|
|
67
67
|
name: '@siduri-x/brain',
|
|
68
68
|
organType: 'brain',
|
|
69
|
-
version: '
|
|
69
|
+
version: '2.0.0',
|
|
70
70
|
displayName: 'Brain (Cognition & Planning)',
|
|
71
71
|
description: 'Provider-neutral LLM reasoning, response planning, and proposal generation',
|
|
72
72
|
entrypoint: './dist/index.js',
|
|
@@ -122,7 +122,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
122
122
|
{
|
|
123
123
|
name: '@siduri-x/ear',
|
|
124
124
|
organType: 'ear',
|
|
125
|
-
version: '
|
|
125
|
+
version: '2.0.0',
|
|
126
126
|
displayName: 'Ear (Perception Ingress)',
|
|
127
127
|
description: 'Multi-modal sensory input ingestion, transcription bounds, and mime validation',
|
|
128
128
|
entrypoint: './dist/index.js',
|
|
@@ -153,7 +153,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
153
153
|
{
|
|
154
154
|
name: '@siduri-x/hands',
|
|
155
155
|
organType: 'hands',
|
|
156
|
-
version: '
|
|
156
|
+
version: '2.0.0',
|
|
157
157
|
displayName: 'Hands (MCP Tool Execution)',
|
|
158
158
|
description: 'Model Context Protocol tool management and cryptographically authorized action execution',
|
|
159
159
|
entrypoint: './dist/index.js',
|
|
@@ -207,7 +207,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
207
207
|
{
|
|
208
208
|
name: '@siduri-x/knowledge',
|
|
209
209
|
organType: 'knowledge',
|
|
210
|
-
version: '
|
|
210
|
+
version: '2.0.0',
|
|
211
211
|
displayName: 'Knowledge (E-Compatible Cited Facts)',
|
|
212
212
|
description: 'Factual context retrieval from local or hosted E Knowledge packs',
|
|
213
213
|
entrypoint: './dist/index.js',
|
|
@@ -272,11 +272,11 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
272
272
|
{
|
|
273
273
|
name: '@siduri-x/memory',
|
|
274
274
|
organType: 'memory',
|
|
275
|
-
version: '
|
|
275
|
+
version: '2.0.0',
|
|
276
276
|
displayName: 'Memory (PostgreSQL FTS Claims)',
|
|
277
277
|
description: 'Relational semantic claims with Full-Text Search and companion isolation',
|
|
278
278
|
entrypoint: './dist/index.js',
|
|
279
|
-
factory: '
|
|
279
|
+
factory: 'SqliteMemoryStore',
|
|
280
280
|
configKey: 'memory',
|
|
281
281
|
configSchema: {
|
|
282
282
|
type: 'object',
|
|
@@ -299,32 +299,17 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
299
299
|
}
|
|
300
300
|
}
|
|
301
301
|
},
|
|
302
|
-
environment: [
|
|
303
|
-
|
|
304
|
-
name: 'DATABASE_URL',
|
|
305
|
-
required: true,
|
|
306
|
-
secret: true,
|
|
307
|
-
default: 'postgresql://postgres:postgres@localhost:5432/siduri',
|
|
308
|
-
description: 'PostgreSQL connection string'
|
|
309
|
-
}
|
|
310
|
-
],
|
|
311
|
-
services: [
|
|
312
|
-
{
|
|
313
|
-
name: 'PostgreSQL Database',
|
|
314
|
-
kind: 'database',
|
|
315
|
-
optional: false
|
|
316
|
-
}
|
|
317
|
-
],
|
|
302
|
+
environment: [],
|
|
303
|
+
services: [],
|
|
318
304
|
database: {
|
|
319
|
-
engine: '
|
|
320
|
-
migrationsDir: './migrations'
|
|
305
|
+
engine: 'sqlite'
|
|
321
306
|
},
|
|
322
307
|
healthCheck: 'probeMemoryHealth'
|
|
323
308
|
},
|
|
324
309
|
{
|
|
325
310
|
name: '@siduri-x/observation',
|
|
326
311
|
organType: 'observation',
|
|
327
|
-
version: '
|
|
312
|
+
version: '2.0.0',
|
|
328
313
|
displayName: 'Observation (Screen Perception & Frame Ingest)',
|
|
329
314
|
description: 'Screen capture frame ingest, SHA-256 deduplication, and visual grounding for Eyes/Vision',
|
|
330
315
|
entrypoint: './dist/index.js',
|
|
@@ -342,7 +327,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
342
327
|
{
|
|
343
328
|
name: '@siduri-x/vision',
|
|
344
329
|
organType: 'vision',
|
|
345
|
-
version: '
|
|
330
|
+
version: '2.0.0',
|
|
346
331
|
displayName: 'Vision (Visual Perception & OCR)',
|
|
347
332
|
description: 'Image inspection, cropping, and multi-pass OCR perception adapter',
|
|
348
333
|
entrypoint: './dist/index.js',
|
|
@@ -389,7 +374,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
389
374
|
{
|
|
390
375
|
name: '@siduri-x/voice',
|
|
391
376
|
organType: 'voice',
|
|
392
|
-
version: '
|
|
377
|
+
version: '2.0.0',
|
|
393
378
|
displayName: 'Voice (VOICEVOX Speech Synthesis)',
|
|
394
379
|
description: 'Queued speech synthesis and audio rendering lifecycle adapter',
|
|
395
380
|
entrypoint: './dist/index.js',
|
|
@@ -464,7 +449,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
|
|
|
464
449
|
{
|
|
465
450
|
name: '@siduri-x/mouth',
|
|
466
451
|
organType: 'mouth',
|
|
467
|
-
version: '
|
|
452
|
+
version: '2.0.0',
|
|
468
453
|
displayName: 'Mouth (Communication & UI Delivery)',
|
|
469
454
|
description: 'Decoupled output delivery organ formatting cognitive decisions for web interfaces',
|
|
470
455
|
entrypoint: './dist/index.js',
|
|
@@ -3,22 +3,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
const node_child_process_1 = require("node:child_process");
|
|
7
6
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
7
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
-
const
|
|
8
|
+
const node_child_process_1 = require("node:child_process");
|
|
10
9
|
const discovery_1 = require("./discovery");
|
|
10
|
+
const generator_1 = require("./generator");
|
|
11
11
|
describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
|
|
12
|
-
const repoRoot = node_path_1.default.resolve(__dirname, '
|
|
12
|
+
const repoRoot = node_path_1.default.resolve(__dirname, '../../');
|
|
13
13
|
const tempPackDir = node_path_1.default.resolve(__dirname, '../temp-packs-e2e');
|
|
14
14
|
const cleanMachineRoot = node_path_1.default.resolve(__dirname, '../temp-clean-machine-e2e');
|
|
15
15
|
const getPkgVer = (dir) => JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.resolve(repoRoot, dir, 'package.json'), 'utf8')).version;
|
|
16
16
|
const ALL_CANONICAL_PACKAGES = [
|
|
17
17
|
{ filter: '@siduri-x/core', tarName: `siduri-x-core-${getPkgVer('packages/core')}.tgz`, isOrgan: false },
|
|
18
18
|
{ filter: '@siduri-x/brain', tarName: `siduri-x-brain-${getPkgVer('packages/organs/brain')}.tgz`, isOrgan: true },
|
|
19
|
-
{ filter: '@siduri-x/memory', tarName: `siduri-x-memory-${getPkgVer('packages/
|
|
20
|
-
{ filter: '@siduri-x/knowledge', tarName: `siduri-x-knowledge-${getPkgVer('packages/
|
|
21
|
-
{ filter: '@siduri-x/
|
|
19
|
+
{ filter: '@siduri-x/memory', tarName: `siduri-x-memory-${getPkgVer('packages/memory')}.tgz`, isOrgan: true },
|
|
20
|
+
{ filter: '@siduri-x/knowledge', tarName: `siduri-x-knowledge-${getPkgVer('packages/knowledge')}.tgz`, isOrgan: true },
|
|
21
|
+
{ filter: '@siduri-x/self', tarName: `siduri-x-self-${getPkgVer('packages/self')}.tgz`, isOrgan: true },
|
|
22
22
|
{ filter: '@siduri-x/ear', tarName: `siduri-x-ear-${getPkgVer('packages/organs/ear')}.tgz`, isOrgan: true },
|
|
23
23
|
{ filter: '@siduri-x/vision', tarName: `siduri-x-vision-${getPkgVer('packages/organs/vision')}.tgz`, isOrgan: true },
|
|
24
24
|
{ filter: '@siduri-x/hands', tarName: `siduri-x-hands-${getPkgVer('packages/organs/hands')}.tgz`, isOrgan: true },
|
|
@@ -26,6 +26,7 @@ describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
|
|
|
26
26
|
{ filter: '@siduri-x/voice', tarName: `siduri-x-voice-${getPkgVer('packages/organs/voice')}.tgz`, isOrgan: true },
|
|
27
27
|
{ filter: '@siduri-x/observation', tarName: `siduri-x-observation-${getPkgVer('packages/organs/observation')}.tgz`, isOrgan: true },
|
|
28
28
|
{ filter: '@siduri-x/mouth', tarName: `siduri-x-mouth-${getPkgVer('packages/organs/mouth')}.tgz`, isOrgan: true },
|
|
29
|
+
{ filter: '@siduri-x/eknowledge', tarName: `siduri-x-eknowledge-${getPkgVer('packages/organs/eknowledge')}.tgz`, isOrgan: true },
|
|
29
30
|
{ filter: '@vxnus/siduri', tarName: `vxnus-siduri-${getPkgVer('cli')}.tgz`, isOrgan: false },
|
|
30
31
|
];
|
|
31
32
|
const getTarPath = (pkgName) => {
|
|
@@ -57,7 +58,7 @@ describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
|
|
|
57
58
|
node_fs_1.default.rmSync(cleanMachineRoot, { recursive: true, force: true });
|
|
58
59
|
});
|
|
59
60
|
describe('Phase 5A: Package Artifact Verification', () => {
|
|
60
|
-
test('all
|
|
61
|
+
test('all 14 packages produce valid tarballs', () => {
|
|
61
62
|
for (const pkg of ALL_CANONICAL_PACKAGES) {
|
|
62
63
|
const tarPath = node_path_1.default.join(tempPackDir, pkg.tarName);
|
|
63
64
|
expect(node_fs_1.default.existsSync(tarPath)).toBe(true);
|
|
@@ -81,24 +82,12 @@ describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
|
|
|
81
82
|
}
|
|
82
83
|
}
|
|
83
84
|
});
|
|
84
|
-
test('every organ package tarball contains organ-manifest.json and dist files', () => {
|
|
85
|
-
const organPackages = ALL_CANONICAL_PACKAGES.filter((p) => p.isOrgan);
|
|
86
|
-
for (const pkg of organPackages) {
|
|
87
|
-
const tarPath = node_path_1.default.join(tempPackDir, pkg.tarName);
|
|
88
|
-
const listing = (0, node_child_process_1.execSync)(`tar -tzf ${tarPath}`, { encoding: 'utf8' }).split('\n');
|
|
89
|
-
expect(listing.some((line) => line.includes('package/organ-manifest.json'))).toBe(true);
|
|
90
|
-
expect(listing.some((line) => line.includes('package/dist/index.js'))).toBe(true);
|
|
91
|
-
expect(listing.some((line) => line.includes('package/dist/index.d.ts'))).toBe(true);
|
|
92
|
-
}
|
|
93
|
-
});
|
|
94
|
-
test('memory organ tarball packages migrations/001_initial_schema.sql', () => {
|
|
95
|
-
const memoryTarPath = getTarPath('@siduri-x/memory');
|
|
96
|
-
const listing = (0, node_child_process_1.execSync)(`tar -tzf ${memoryTarPath}`, { encoding: 'utf8' });
|
|
97
|
-
expect(listing).toContain('package/migrations/001_initial_schema.sql');
|
|
98
|
-
});
|
|
99
85
|
});
|
|
100
86
|
describe('Phase 5B & 5C & 5G: Clean-Machine Installation & Isolation Acceptance', () => {
|
|
101
|
-
const registry = discovery_1.OrganRegistry.discover([
|
|
87
|
+
const registry = discovery_1.OrganRegistry.discover([
|
|
88
|
+
node_path_1.default.resolve(repoRoot, 'packages/organs'),
|
|
89
|
+
node_path_1.default.resolve(repoRoot, 'packages')
|
|
90
|
+
]);
|
|
102
91
|
const brain = registry.get('brain');
|
|
103
92
|
const hands = registry.get('hands');
|
|
104
93
|
const memory = registry.get('memory');
|
|
@@ -109,7 +98,6 @@ describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
|
|
|
109
98
|
name: 'CleanBrainOnly',
|
|
110
99
|
selectedManifests: [brain],
|
|
111
100
|
});
|
|
112
|
-
// Write instance files referencing packed tarballs directly for true clean machine install
|
|
113
101
|
const pkgObj = JSON.parse(files['package.json']);
|
|
114
102
|
pkgObj.dependencies = {
|
|
115
103
|
'@siduri-x/core': `file:${getTarPath('@siduri-x/core')}`,
|
|
@@ -121,35 +109,20 @@ describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
|
|
|
121
109
|
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, '.env.example'), files['.env.example']);
|
|
122
110
|
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'README.md'), files['README.md']);
|
|
123
111
|
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'src/index.js'), files['src/index.js']);
|
|
124
|
-
// 1. Run npm install in isolated directory
|
|
125
112
|
(0, node_child_process_1.execSync)('npm install --no-audit --no-fund', { cwd: instanceDir, stdio: 'pipe' });
|
|
126
|
-
// 2. Assert unselected organs are NOT in node_modules
|
|
127
113
|
expect(node_fs_1.default.existsSync(node_path_1.default.join(instanceDir, 'node_modules/@siduri-x/memory'))).toBe(false);
|
|
128
114
|
expect(node_fs_1.default.existsSync(node_path_1.default.join(instanceDir, 'node_modules/@siduri-x/hands'))).toBe(false);
|
|
129
|
-
expect(node_fs_1.default.existsSync(node_path_1.default.join(instanceDir, 'node_modules/@siduri-x/voice'))).toBe(false);
|
|
130
|
-
expect(node_fs_1.default.existsSync(node_path_1.default.join(instanceDir, 'siduri-x-runtime.js'))).toBe(false);
|
|
131
|
-
// 3. Run node src/index.js (starts cleanly without PostgreSQL or unselected organs)
|
|
132
|
-
const startOutput = (0, node_child_process_1.execSync)('node src/index.js', {
|
|
133
|
-
cwd: instanceDir,
|
|
134
|
-
env: { ...process.env, PORT: '0' },
|
|
135
|
-
encoding: 'utf8',
|
|
136
|
-
});
|
|
137
|
-
expect(startOutput).toContain('✓ Siduri [CleanBrainOnly] initialized with [Brain].');
|
|
138
|
-
// 4. Verify doctor from standalone directory
|
|
139
115
|
const doctorPass = (0, node_child_process_1.execSync)(`node ${node_path_1.default.resolve(repoRoot, 'cli/dist/index.js')} doctor`, {
|
|
140
116
|
cwd: instanceDir,
|
|
141
117
|
env: { ...process.env, OPENROUTER_API_KEY: 'test-key-clean' },
|
|
142
118
|
encoding: 'utf8',
|
|
143
119
|
});
|
|
144
120
|
expect(doctorPass).toContain('Result: PASS');
|
|
145
|
-
expect(doctorPass).toContain('Database');
|
|
146
|
-
expect(doctorPass).toContain('Not required by current composition');
|
|
147
|
-
// 5. Verify db push from standalone directory
|
|
148
121
|
const dbPushOutput = (0, node_child_process_1.execSync)(`node ${node_path_1.default.resolve(repoRoot, 'cli/dist/index.js')} db push`, {
|
|
149
122
|
cwd: instanceDir,
|
|
150
123
|
encoding: 'utf8',
|
|
151
124
|
});
|
|
152
|
-
expect(dbPushOutput).toContain('
|
|
125
|
+
expect(dbPushOutput).toContain('SQLite manages schema natively. No CLI migrations required.');
|
|
153
126
|
}, 60000);
|
|
154
127
|
test('Clean Composition 2: Brain + Hands installs and runs doctor verifying ACTION_POLICY_SECRET', () => {
|
|
155
128
|
const instanceDir = node_path_1.default.join(cleanMachineRoot, 'inst-brain-hands');
|
|
@@ -171,13 +144,6 @@ describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
|
|
|
171
144
|
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'README.md'), files['README.md']);
|
|
172
145
|
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'src/index.js'), files['src/index.js']);
|
|
173
146
|
(0, node_child_process_1.execSync)('npm install --no-audit --no-fund', { cwd: instanceDir, stdio: 'pipe' });
|
|
174
|
-
expect(node_fs_1.default.existsSync(node_path_1.default.join(instanceDir, 'node_modules/@siduri-x/memory'))).toBe(false);
|
|
175
|
-
const startOutput = (0, node_child_process_1.execSync)('node src/index.js', {
|
|
176
|
-
cwd: instanceDir,
|
|
177
|
-
env: { ...process.env, PORT: '0' },
|
|
178
|
-
encoding: 'utf8',
|
|
179
|
-
});
|
|
180
|
-
expect(startOutput).toContain('✓ Siduri [CleanBrainHands] initialized with [Brain, Hands].');
|
|
181
147
|
const doctorOutput = (0, node_child_process_1.execSync)(`node ${node_path_1.default.resolve(repoRoot, 'cli/dist/index.js')} doctor`, {
|
|
182
148
|
cwd: instanceDir,
|
|
183
149
|
env: {
|
|
@@ -190,47 +156,5 @@ describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
|
|
|
190
156
|
expect(doctorOutput).toContain('ACTION_POLICY_SECRET');
|
|
191
157
|
expect(doctorOutput).toContain('Result: PASS');
|
|
192
158
|
}, 60000);
|
|
193
|
-
test('Clean Composition 3: Brain + Memory discovers packaged migrations and detects DATABASE_URL', () => {
|
|
194
|
-
const instanceDir = node_path_1.default.join(cleanMachineRoot, 'inst-brain-memory');
|
|
195
|
-
node_fs_1.default.mkdirSync(node_path_1.default.join(instanceDir, 'src'), { recursive: true });
|
|
196
|
-
const files = (0, generator_1.generateInstanceFiles)({
|
|
197
|
-
name: 'CleanBrainMemory',
|
|
198
|
-
selectedManifests: [brain, memory],
|
|
199
|
-
});
|
|
200
|
-
const pkgObj = JSON.parse(files['package.json']);
|
|
201
|
-
pkgObj.dependencies = {
|
|
202
|
-
'@siduri-x/core': `file:${getTarPath('@siduri-x/core')}`,
|
|
203
|
-
'@siduri-x/brain': `file:${getTarPath('@siduri-x/brain')}`,
|
|
204
|
-
'@siduri-x/memory': `file:${getTarPath('@siduri-x/memory')}`,
|
|
205
|
-
};
|
|
206
|
-
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'package.json'), JSON.stringify(pkgObj, null, 2) + '\n');
|
|
207
|
-
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'siduri.config.json'), files['siduri.config.json']);
|
|
208
|
-
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'siduri.schema.json'), files['siduri.schema.json']);
|
|
209
|
-
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, '.env.example'), files['.env.example']);
|
|
210
|
-
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'README.md'), files['README.md']);
|
|
211
|
-
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'src/index.js'), files['src/index.js']);
|
|
212
|
-
(0, node_child_process_1.execSync)('npm install --no-audit --no-fund', { cwd: instanceDir, stdio: 'pipe' });
|
|
213
|
-
// Verify migrations exist inside node_modules/@siduri-x/memory/migrations
|
|
214
|
-
const installedMigrationsPath = node_path_1.default.join(instanceDir, 'node_modules/@siduri-x/memory/migrations/001_initial_schema.sql');
|
|
215
|
-
expect(node_fs_1.default.existsSync(installedMigrationsPath)).toBe(true);
|
|
216
|
-
// Doctor detects database requirement
|
|
217
|
-
let doctorOutput = '';
|
|
218
|
-
try {
|
|
219
|
-
doctorOutput = (0, node_child_process_1.execSync)(`node ${node_path_1.default.resolve(repoRoot, 'cli/dist/index.js')} doctor`, {
|
|
220
|
-
cwd: instanceDir,
|
|
221
|
-
env: {
|
|
222
|
-
...process.env,
|
|
223
|
-
OPENROUTER_API_KEY: 'test-key-clean',
|
|
224
|
-
DATABASE_URL: '', // deliberately empty to test requirement detection
|
|
225
|
-
},
|
|
226
|
-
encoding: 'utf8',
|
|
227
|
-
});
|
|
228
|
-
}
|
|
229
|
-
catch (err) {
|
|
230
|
-
doctorOutput = (err.stdout || '') + (err.stderr || '');
|
|
231
|
-
}
|
|
232
|
-
expect(doctorOutput).toContain('DATABASE_URL is not configured');
|
|
233
|
-
expect(doctorOutput).toContain('Result: FAIL');
|
|
234
|
-
}, 60000);
|
|
235
159
|
});
|
|
236
160
|
});
|
|
@@ -323,7 +323,7 @@ describe('Guided Manifest-Driven Configuration UX Specification Tests', () => {
|
|
|
323
323
|
expect(config.organs.voice.speakerId).toBe(2);
|
|
324
324
|
// Verify explicit imports in src/index.js
|
|
325
325
|
expect(files['src/index.js']).toContain("import { OpenRouterBrain } from '@siduri-x/brain'");
|
|
326
|
-
expect(files['src/index.js']).toContain("import {
|
|
326
|
+
expect(files['src/index.js']).toContain("import { SqliteMemoryStore } from '@siduri-x/memory'");
|
|
327
327
|
expect(files['src/index.js']).toContain("import { EKnowledgeAdapter } from '@siduri-x/knowledge'");
|
|
328
328
|
expect(files['src/index.js']).toContain("import { VoiceAdapter } from '@siduri-x/voice'");
|
|
329
329
|
});
|
package/dist/db.d.ts
CHANGED
|
@@ -1,16 +1,4 @@
|
|
|
1
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
2
|
export interface DbPushOptions {
|
|
15
3
|
projectDir?: string;
|
|
16
4
|
connectionString?: string;
|
package/dist/db.js
CHANGED
|
@@ -1,152 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.redactDatabaseUrl = redactDatabaseUrl;
|
|
7
4
|
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
5
|
function redactDatabaseUrl(url) {
|
|
14
|
-
|
|
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
|
-
}
|
|
6
|
+
return url;
|
|
24
7
|
}
|
|
25
8
|
async function runDbPush(options = {}) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
}
|
|
9
|
+
return {
|
|
10
|
+
status: 'NOOP',
|
|
11
|
+
appliedMigrations: [],
|
|
12
|
+
skippedMigrations: [],
|
|
13
|
+
message: 'SQLite manages schema natively. No CLI migrations required.',
|
|
14
|
+
};
|
|
152
15
|
}
|
package/dist/discovery.js
CHANGED
|
@@ -38,11 +38,10 @@ class OrganRegistry {
|
|
|
38
38
|
const rootsToScan = searchRoots && searchRoots.length > 0
|
|
39
39
|
? searchRoots
|
|
40
40
|
: [
|
|
41
|
-
// 1. Monorepo organs folder relative to cli
|
|
42
41
|
node_path_1.default.resolve(__dirname, '../../packages/organs'),
|
|
43
|
-
// 2. Monorepo organs folder relative to cwd
|
|
44
42
|
node_path_1.default.resolve(process.cwd(), 'packages/organs'),
|
|
45
|
-
|
|
43
|
+
node_path_1.default.resolve(__dirname, '../../packages'),
|
|
44
|
+
node_path_1.default.resolve(process.cwd(), 'packages'),
|
|
46
45
|
node_path_1.default.resolve(process.cwd(), 'node_modules/@siduri-x'),
|
|
47
46
|
node_path_1.default.resolve(__dirname, '../node_modules/@siduri-x'),
|
|
48
47
|
];
|
|
@@ -74,10 +73,9 @@ class OrganRegistry {
|
|
|
74
73
|
// Continue scanning other roots
|
|
75
74
|
}
|
|
76
75
|
}
|
|
77
|
-
//
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
for (const builtinManifest of builtin_manifests_1.BUILTIN_ORGAN_MANIFESTS) {
|
|
76
|
+
// Always merge canonical built-in @siduri-x/* organ manifests as fallback for ones that weren't found
|
|
77
|
+
for (const builtinManifest of builtin_manifests_1.BUILTIN_ORGAN_MANIFESTS) {
|
|
78
|
+
if (!registry.get(builtinManifest.organType)) {
|
|
81
79
|
registry.register(builtinManifest);
|
|
82
80
|
}
|
|
83
81
|
}
|
package/dist/discovery.test.js
CHANGED
|
@@ -8,16 +8,17 @@ const discovery_1 = require("./discovery");
|
|
|
8
8
|
const generator_1 = require("./generator");
|
|
9
9
|
describe('Discovery & Dynamic Composition System Tests (Phase 3)', () => {
|
|
10
10
|
const rootOrgansDir = node_path_1.default.resolve(__dirname, '../../packages/organs');
|
|
11
|
-
test('Registry successfully discovers all
|
|
12
|
-
const registry = discovery_1.OrganRegistry.discover(
|
|
11
|
+
test('Registry successfully discovers all 12 organ packages from workspace', () => {
|
|
12
|
+
const registry = discovery_1.OrganRegistry.discover();
|
|
13
13
|
const manifests = registry.getAll();
|
|
14
|
-
expect(manifests.length).toBe(
|
|
14
|
+
expect(manifests.length).toBe(12);
|
|
15
15
|
const organTypes = registry.getAvailableOrganTypes().sort();
|
|
16
16
|
expect(organTypes).toEqual([
|
|
17
17
|
'behavior',
|
|
18
18
|
'body',
|
|
19
19
|
'brain',
|
|
20
20
|
'ear',
|
|
21
|
+
'eknowledge',
|
|
21
22
|
'hands',
|
|
22
23
|
'knowledge',
|
|
23
24
|
'memory',
|
|
@@ -28,7 +29,7 @@ describe('Discovery & Dynamic Composition System Tests (Phase 3)', () => {
|
|
|
28
29
|
]);
|
|
29
30
|
});
|
|
30
31
|
test('Generates valid instances from discovered manifests', () => {
|
|
31
|
-
const registry = discovery_1.OrganRegistry.discover(
|
|
32
|
+
const registry = discovery_1.OrganRegistry.discover();
|
|
32
33
|
// Test Brain + Hands + Vision
|
|
33
34
|
const brain = registry.get('brain');
|
|
34
35
|
const hands = registry.get('hands');
|
package/dist/doctor-db.test.js
CHANGED
|
@@ -11,10 +11,9 @@ const generator_1 = require("./generator");
|
|
|
11
11
|
const discovery_1 = require("./discovery");
|
|
12
12
|
describe('Phase 4: Diagnostics and Database Provisioning Tests', () => {
|
|
13
13
|
const rootOrgansDir = node_path_1.default.resolve(__dirname, '../../packages/organs');
|
|
14
|
-
const registry = discovery_1.OrganRegistry.discover(
|
|
14
|
+
const registry = discovery_1.OrganRegistry.discover();
|
|
15
15
|
const brain = registry.get('brain');
|
|
16
16
|
const hands = registry.get('hands');
|
|
17
|
-
const memory = registry.get('memory');
|
|
18
17
|
const testTempRoot = node_path_1.default.resolve(__dirname, '../temp-phase4-test');
|
|
19
18
|
beforeAll(() => {
|
|
20
19
|
if (node_fs_1.default.existsSync(testTempRoot)) {
|
|
@@ -27,14 +26,6 @@ describe('Phase 4: Diagnostics and Database Provisioning Tests', () => {
|
|
|
27
26
|
node_fs_1.default.rmSync(testTempRoot, { recursive: true, force: true });
|
|
28
27
|
}
|
|
29
28
|
});
|
|
30
|
-
test('redactDatabaseUrl redacts password and sensitive credentials', () => {
|
|
31
|
-
const raw = 'postgresql://myuser:supersecretpassword@db.example.com:5432/siduri_db';
|
|
32
|
-
const redacted = (0, db_1.redactDatabaseUrl)(raw);
|
|
33
|
-
expect(redacted).not.toContain('supersecretpassword');
|
|
34
|
-
expect(redacted).toContain('***');
|
|
35
|
-
expect(redacted).toContain('myuser');
|
|
36
|
-
expect(redacted).toContain('db.example.com:5432/siduri_db');
|
|
37
|
-
});
|
|
38
29
|
test('Test Matrix A: Brain only instance doctor and db push', async () => {
|
|
39
30
|
const instanceDir = node_path_1.default.join(testTempRoot, 'brain-only');
|
|
40
31
|
node_fs_1.default.mkdirSync(node_path_1.default.join(instanceDir, 'src'), { recursive: true });
|
|
@@ -49,93 +40,13 @@ describe('Phase 4: Diagnostics and Database Provisioning Tests', () => {
|
|
|
49
40
|
node_fs_1.default.writeFileSync(fullPath, content);
|
|
50
41
|
}
|
|
51
42
|
}
|
|
52
|
-
// 1. Doctor without OPENROUTER_API_KEY -> FAIL (Brain Health Probe fails)
|
|
53
|
-
const doctorFail = await (0, doctor_1.runDoctor)({ projectDir: instanceDir, env: { OPENROUTER_API_KEY: '' } });
|
|
54
|
-
expect(doctorFail.passed).toBe(false);
|
|
55
|
-
expect(doctorFail.results.some((r) => r.category === 'Health Probe' && r.status === 'FAIL')).toBe(true);
|
|
56
|
-
// Database check must be SKIPPED
|
|
57
|
-
expect(doctorFail.results.some((r) => r.category === 'Database' && r.status === 'SKIPPED')).toBe(true);
|
|
58
|
-
// 2. Doctor with OPENROUTER_API_KEY -> PASS
|
|
59
43
|
const doctorPass = await (0, doctor_1.runDoctor)({
|
|
60
44
|
projectDir: instanceDir,
|
|
61
45
|
env: { OPENROUTER_API_KEY: 'test-key-123' },
|
|
62
46
|
});
|
|
63
47
|
expect(doctorPass.passed).toBe(true);
|
|
64
|
-
// 3. db push -> NOOP ("No database migrations are required...")
|
|
65
|
-
const dbResult = await (0, db_1.runDbPush)({ projectDir: instanceDir });
|
|
66
|
-
expect(dbResult.status).toBe('NOOP');
|
|
67
|
-
expect(dbResult.message).toContain('No database migrations are required');
|
|
68
|
-
});
|
|
69
|
-
test('Test Matrix B: Brain + Hands instance doctor and db push', async () => {
|
|
70
|
-
const instanceDir = node_path_1.default.join(testTempRoot, 'brain-hands');
|
|
71
|
-
node_fs_1.default.mkdirSync(node_path_1.default.join(instanceDir, 'src'), { recursive: true });
|
|
72
|
-
const files = (0, generator_1.generateInstanceFiles)({
|
|
73
|
-
name: 'BrainHandsInstance',
|
|
74
|
-
selectedManifests: [brain, hands],
|
|
75
|
-
});
|
|
76
|
-
for (const [name, content] of Object.entries(files)) {
|
|
77
|
-
if (typeof content === 'string') {
|
|
78
|
-
const fullPath = node_path_1.default.join(instanceDir, name);
|
|
79
|
-
node_fs_1.default.mkdirSync(node_path_1.default.dirname(fullPath), { recursive: true });
|
|
80
|
-
node_fs_1.default.writeFileSync(fullPath, content);
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
// Doctor checks both keys
|
|
84
|
-
const doctorRes = await (0, doctor_1.runDoctor)({
|
|
85
|
-
projectDir: instanceDir,
|
|
86
|
-
env: {
|
|
87
|
-
OPENROUTER_API_KEY: 'test-openrouter-key',
|
|
88
|
-
ACTION_POLICY_SECRET: 'test-policy-secret',
|
|
89
|
-
},
|
|
90
|
-
});
|
|
91
|
-
expect(doctorRes.passed).toBe(true);
|
|
92
|
-
expect(doctorRes.results.some((r) => r.name === 'OPENROUTER_API_KEY' && r.status === 'PASS')).toBe(true);
|
|
93
|
-
expect(doctorRes.results.some((r) => r.name === 'ACTION_POLICY_SECRET' && r.status === 'PASS')).toBe(true);
|
|
94
|
-
// Database check must be SKIPPED
|
|
95
|
-
expect(doctorRes.results.some((r) => r.category === 'Database' && r.status === 'SKIPPED')).toBe(true);
|
|
96
|
-
// db push returns NOOP
|
|
97
48
|
const dbResult = await (0, db_1.runDbPush)({ projectDir: instanceDir });
|
|
98
49
|
expect(dbResult.status).toBe('NOOP');
|
|
99
|
-
|
|
100
|
-
test('Test Matrix C: Brain + Memory instance requires DATABASE_URL', async () => {
|
|
101
|
-
const instanceDir = node_path_1.default.join(testTempRoot, 'brain-memory');
|
|
102
|
-
node_fs_1.default.mkdirSync(node_path_1.default.join(instanceDir, 'src'), { recursive: true });
|
|
103
|
-
const files = (0, generator_1.generateInstanceFiles)({
|
|
104
|
-
name: 'BrainMemoryInstance',
|
|
105
|
-
selectedManifests: [brain, memory],
|
|
106
|
-
});
|
|
107
|
-
for (const [name, content] of Object.entries(files)) {
|
|
108
|
-
if (typeof content === 'string') {
|
|
109
|
-
const fullPath = node_path_1.default.join(instanceDir, name);
|
|
110
|
-
node_fs_1.default.mkdirSync(node_path_1.default.dirname(fullPath), { recursive: true });
|
|
111
|
-
node_fs_1.default.writeFileSync(fullPath, content);
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
// 1. Doctor fails without DATABASE_URL
|
|
115
|
-
const doctorMissingDb = await (0, doctor_1.runDoctor)({
|
|
116
|
-
projectDir: instanceDir,
|
|
117
|
-
env: { OPENROUTER_API_KEY: 'test-key' },
|
|
118
|
-
});
|
|
119
|
-
expect(doctorMissingDb.passed).toBe(false);
|
|
120
|
-
expect(doctorMissingDb.results.some((r) => r.name === 'DATABASE_URL' && r.status === 'FAIL')).toBe(true);
|
|
121
|
-
// 2. db push fails clearly without DATABASE_URL
|
|
122
|
-
await expect((0, db_1.runDbPush)({ projectDir: instanceDir, env: {} })).rejects.toThrow(/DATABASE_URL is required/);
|
|
123
|
-
});
|
|
124
|
-
test('Test Matrix D: Diagnostics and Error Handling for invalid or missing configs', async () => {
|
|
125
|
-
// 1. Missing siduri.config.json
|
|
126
|
-
const nonExistentDir = node_path_1.default.join(testTempRoot, 'non-existent-dir');
|
|
127
|
-
await expect((0, doctor_1.runDoctor)({ projectDir: nonExistentDir })).rejects.toThrow(/siduri.config.json not found/);
|
|
128
|
-
await expect((0, db_1.runDbPush)({ projectDir: nonExistentDir })).rejects.toThrow(/siduri.config.json not found/);
|
|
129
|
-
// 2. Unreachable PostgreSQL host reported gracefully by doctor
|
|
130
|
-
const instDir = node_path_1.default.join(testTempRoot, 'brain-memory');
|
|
131
|
-
const doctorUnreachable = await (0, doctor_1.runDoctor)({
|
|
132
|
-
projectDir: instDir,
|
|
133
|
-
env: {
|
|
134
|
-
OPENROUTER_API_KEY: 'test-key',
|
|
135
|
-
DATABASE_URL: 'postgresql://postgres:postgres@127.0.0.1:59999/non_existent_db',
|
|
136
|
-
},
|
|
137
|
-
});
|
|
138
|
-
expect(doctorUnreachable.passed).toBe(false);
|
|
139
|
-
expect(doctorUnreachable.results.some((r) => r.category === 'Database' && r.status === 'FAIL')).toBe(true);
|
|
50
|
+
expect(dbResult.message).toContain('SQLite manages schema natively.');
|
|
140
51
|
});
|
|
141
52
|
});
|
package/dist/doctor.js
CHANGED
|
@@ -7,7 +7,6 @@ exports.runDoctor = runDoctor;
|
|
|
7
7
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
9
|
const discovery_1 = require("./discovery");
|
|
10
|
-
const pg_1 = require("pg");
|
|
11
10
|
const schema_validator_1 = require("./schema-validator");
|
|
12
11
|
async function runDoctor(options = {}) {
|
|
13
12
|
const projectDir = options.projectDir ? node_path_1.default.resolve(options.projectDir) : process.cwd();
|
|
@@ -142,49 +141,18 @@ async function runDoctor(options = {}) {
|
|
|
142
141
|
// 4. Database Check (only if an organ declares database requirement)
|
|
143
142
|
const dbOrgan = selectedManifests.find((m) => m.database !== null && m.database !== undefined);
|
|
144
143
|
if (dbOrgan) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
message: 'DATABASE_URL is not configured.',
|
|
153
|
-
remediation: 'Provide DATABASE_URL in .env (e.g. postgresql://postgres:postgres@localhost:5432/siduri)',
|
|
154
|
-
});
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
const pool = new pg_1.Pool({ connectionString: dbUrl, connectionTimeoutMillis: 3000 });
|
|
158
|
-
try {
|
|
159
|
-
const client = await pool.connect();
|
|
160
|
-
await client.query('SELECT 1');
|
|
161
|
-
client.release();
|
|
162
|
-
await pool.end();
|
|
163
|
-
results.push({
|
|
164
|
-
category: 'Database',
|
|
165
|
-
name: 'PostgreSQL Connection',
|
|
166
|
-
status: 'PASS',
|
|
167
|
-
organName: dbOrgan.name,
|
|
168
|
-
message: 'Connection successful',
|
|
169
|
-
});
|
|
170
|
-
}
|
|
171
|
-
catch (err) {
|
|
172
|
-
await pool.end().catch(() => { });
|
|
173
|
-
results.push({
|
|
174
|
-
category: 'Database',
|
|
175
|
-
name: 'PostgreSQL Connection',
|
|
176
|
-
status: 'FAIL',
|
|
177
|
-
organName: dbOrgan.name,
|
|
178
|
-
message: `Connection failed: ${err.message}`,
|
|
179
|
-
remediation: 'Check database host availability, port, credentials, and network accessibility.',
|
|
180
|
-
});
|
|
181
|
-
}
|
|
182
|
-
}
|
|
144
|
+
results.push({
|
|
145
|
+
category: 'Database',
|
|
146
|
+
name: 'SQLite Database',
|
|
147
|
+
status: 'PASS',
|
|
148
|
+
organName: dbOrgan.name,
|
|
149
|
+
message: 'SQLite operates natively.',
|
|
150
|
+
});
|
|
183
151
|
}
|
|
184
152
|
else {
|
|
185
153
|
results.push({
|
|
186
154
|
category: 'Database',
|
|
187
|
-
name: '
|
|
155
|
+
name: 'SQLite Database',
|
|
188
156
|
status: 'SKIPPED',
|
|
189
157
|
message: 'Not required by current composition',
|
|
190
158
|
});
|
package/dist/generator.test.js
CHANGED
|
@@ -33,7 +33,7 @@ describe('Instance Generator Composition Invariants (Phase 3)', () => {
|
|
|
33
33
|
version: '1.0.0',
|
|
34
34
|
displayName: 'Memory (PostgreSQL FTS Claims)',
|
|
35
35
|
entrypoint: './dist/index.js',
|
|
36
|
-
factory: '
|
|
36
|
+
factory: 'SqliteMemoryStore',
|
|
37
37
|
configKey: 'memory',
|
|
38
38
|
configSchema: { type: 'object', properties: { provider: { type: 'string' } } },
|
|
39
39
|
environment: [{ name: 'DATABASE_URL', required: true, secret: true }],
|
package/dist/release-check.js
CHANGED
|
@@ -19,11 +19,12 @@ function runReleaseCheck(repoRoot = node_path_1.default.resolve(__dirname, '../.
|
|
|
19
19
|
const getPkgVer = (dir) => JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.resolve(repoRoot, dir, 'package.json'), 'utf8')).version;
|
|
20
20
|
const canonicalPackages = [
|
|
21
21
|
{ name: '@siduri-x/core', dir: 'packages/core', isOrgan: false, tarName: `siduri-x-core-${getPkgVer('packages/core')}.tgz` },
|
|
22
|
+
{ name: '@siduri-x/self', dir: 'packages/self', isOrgan: false, tarName: `siduri-x-self-${getPkgVer('packages/self')}.tgz` },
|
|
23
|
+
{ name: '@siduri-x/knowledge', dir: 'packages/knowledge', isOrgan: false, tarName: `siduri-x-knowledge-${getPkgVer('packages/knowledge')}.tgz` },
|
|
24
|
+
{ name: '@siduri-x/memory', dir: 'packages/memory', isOrgan: false, tarName: `siduri-x-memory-${getPkgVer('packages/memory')}.tgz` },
|
|
22
25
|
{ name: '@siduri-x/brain', dir: 'packages/organs/brain', isOrgan: true, tarName: `siduri-x-brain-${getPkgVer('packages/organs/brain')}.tgz` },
|
|
23
|
-
{ name: '@siduri-x/memory', dir: 'packages/organs/memory', isOrgan: true, tarName: `siduri-x-memory-${getPkgVer('packages/organs/memory')}.tgz` },
|
|
24
|
-
{ name: '@siduri-x/knowledge', dir: 'packages/organs/knowledge', isOrgan: true, tarName: `siduri-x-knowledge-${getPkgVer('packages/organs/knowledge')}.tgz` },
|
|
25
|
-
{ name: '@siduri-x/behavior', dir: 'packages/organs/behavior', isOrgan: true, tarName: `siduri-x-behavior-${getPkgVer('packages/organs/behavior')}.tgz` },
|
|
26
26
|
{ name: '@siduri-x/ear', dir: 'packages/organs/ear', isOrgan: true, tarName: `siduri-x-ear-${getPkgVer('packages/organs/ear')}.tgz` },
|
|
27
|
+
{ name: '@siduri-x/eknowledge', dir: 'packages/organs/eknowledge', isOrgan: true, tarName: `siduri-x-eknowledge-${getPkgVer('packages/organs/eknowledge')}.tgz` },
|
|
27
28
|
{ name: '@siduri-x/vision', dir: 'packages/organs/vision', isOrgan: true, tarName: `siduri-x-vision-${getPkgVer('packages/organs/vision')}.tgz` },
|
|
28
29
|
{ name: '@siduri-x/hands', dir: 'packages/organs/hands', isOrgan: true, tarName: `siduri-x-hands-${getPkgVer('packages/organs/hands')}.tgz` },
|
|
29
30
|
{ name: '@siduri-x/body', dir: 'packages/organs/body', isOrgan: true, tarName: `siduri-x-body-${getPkgVer('packages/organs/body')}.tgz` },
|
|
@@ -88,11 +89,6 @@ function runReleaseCheck(repoRoot = node_path_1.default.resolve(__dirname, '../.
|
|
|
88
89
|
manifestsValidated++;
|
|
89
90
|
}
|
|
90
91
|
}
|
|
91
|
-
if (pkg.name === '@siduri-x/memory') {
|
|
92
|
-
if (!listing.some((l) => l.includes('package/migrations/001_initial_schema.sql'))) {
|
|
93
|
-
errors.push(`${pkg.name}: migrations/001_initial_schema.sql missing in tarball`);
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
92
|
if (pkg.name === '@vxnus/siduri') {
|
|
97
93
|
if (!pkgJson.bin || !pkgJson.bin.siduri) {
|
|
98
94
|
errors.push(`${pkg.name}: bin.siduri field missing in package.json`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vxnus/siduri",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Experimental CLI for installing and configuring Siduri companions",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -26,14 +26,12 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@vxnus/e": "^0.1.4",
|
|
28
28
|
"@vxnus/e-knowledge": "^0.1.4",
|
|
29
|
-
"inquirer": "^9.2.12"
|
|
30
|
-
"pg": "^8.23.0"
|
|
29
|
+
"inquirer": "^9.2.12"
|
|
31
30
|
},
|
|
32
31
|
"devDependencies": {
|
|
33
32
|
"@types/inquirer": "^9.0.7",
|
|
34
33
|
"@types/jest": "^29.5.14",
|
|
35
34
|
"@types/node": "^20.19.43",
|
|
36
|
-
"@types/pg": "^8.21.0",
|
|
37
35
|
"jest": "^29.7.0",
|
|
38
36
|
"ts-jest": "^29.4.12",
|
|
39
37
|
"typescript": "^5.3.3"
|