@vxnus/siduri 0.1.11 → 2.0.1
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 +7 -7
- package/dist/builtin-manifests.js +42 -51
- package/dist/clean-machine-e2e.test.js +25 -101
- package/dist/clean-machine-smoke.test.d.ts +1 -0
- package/dist/clean-machine-smoke.test.js +166 -0
- package/dist/configurators/knowledge.js +134 -46
- package/dist/configurators/memory.js +7 -25
- package/dist/configurators.test.js +12 -13
- 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 +53 -41
- package/dist/generator.js +91 -15
- package/dist/generator.test.js +10 -8
- package/dist/index.d.ts +1 -1
- package/dist/index.js +37 -32
- package/dist/manifest.d.ts +1 -1
- package/dist/providers/openrouter.js +1 -1
- package/dist/release-check.js +5 -9
- package/dist/web-dist/404/index.html +1 -1
- package/dist/web-dist/404.html +1 -1
- package/dist/web-dist/__next.__PAGE__.txt +12 -12
- package/dist/web-dist/__next._full.txt +12 -12
- package/dist/web-dist/__next._tree.txt +1 -1
- package/dist/web-dist/_next/static/chunks/0jgw-jf26m0ui.js +22 -0
- package/dist/web-dist/_next/static/chunks/0tdcnq1-n6kof.js +31 -0
- package/dist/web-dist/_next/static/chunks/12vwhiuvdy_i7.js +1 -0
- package/dist/web-dist/_next/static/chunks/{2_duynll0m51m.js → 15zsrb5462n_-.js} +2 -2
- package/dist/web-dist/_next/static/chunks/2dsfgoa225hpl.js +1 -0
- package/dist/web-dist/_next/static/chunks/2mno6yfdoee0x.js +1 -0
- package/dist/web-dist/_next/static/chunks/30a48dk5bjyhj.js +1 -0
- package/dist/web-dist/_next/static/chunks/360luxuz2x8y_.js +1 -0
- package/dist/web-dist/_next/static/chunks/turbopack-2hpygfb68u21h.js +1 -0
- package/dist/web-dist/_not-found/__next._full.txt +8 -8
- package/dist/web-dist/_not-found/__next._not-found.__PAGE__.txt +7 -7
- package/dist/web-dist/_not-found/__next._tree.txt +1 -1
- package/dist/web-dist/_not-found/index.html +1 -1
- package/dist/web-dist/_not-found/index.txt +8 -8
- package/dist/web-dist/chat/__next._full.txt +9 -9
- package/dist/web-dist/chat/__next._tree.txt +1 -1
- package/dist/web-dist/chat/__next.chat.__PAGE__.txt +8 -8
- package/dist/web-dist/chat/index.html +1 -1
- package/dist/web-dist/chat/index.txt +9 -9
- package/dist/web-dist/index.html +1 -1
- package/dist/web-dist/index.txt +12 -12
- package/dist/web-dist/operator/__next._full.txt +9 -9
- package/dist/web-dist/operator/__next._tree.txt +1 -1
- package/dist/web-dist/operator/__next.operator.__PAGE__.txt +8 -8
- package/dist/web-dist/operator/index.html +1 -1
- package/dist/web-dist/operator/index.txt +9 -9
- package/dist/web-template.js +1 -1
- package/package.json +10 -12
- package/dist/web-dist/_next/static/chunks/09y7khbmco_v4.js +0 -1
- package/dist/web-dist/_next/static/chunks/0uwih7sydv413.js +0 -1
- package/dist/web-dist/_next/static/chunks/1_yae15ffa5f6.js +0 -1
- package/dist/web-dist/_next/static/chunks/2kcqevsmv6kh2.js +0 -31
- package/dist/web-dist/_next/static/chunks/2s3d1bfr0l4b0.js +0 -1
- package/dist/web-dist/_next/static/chunks/32z87id9k6i2z.js +0 -22
- package/dist/web-dist/_next/static/chunks/3bx2gs3idjmo1.js +0 -1
- package/dist/web-dist/_next/static/chunks/turbopack-3eipe4c-imn5l.js +0 -1
- /package/dist/web-dist/_next/static/{pWHUixFDDvANZeAf-shqT → lQfIAv021hLk8KVAElf2K}/_buildManifest.js +0 -0
- /package/dist/web-dist/_next/static/{pWHUixFDDvANZeAf-shqT → lQfIAv021hLk8KVAElf2K}/_clientMiddlewareManifest.js +0 -0
- /package/dist/web-dist/_next/static/{pWHUixFDDvANZeAf-shqT → lQfIAv021hLk8KVAElf2K}/_ssgManifest.js +0 -0
|
@@ -5,15 +5,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.configureKnowledge = configureKnowledge;
|
|
7
7
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
9
|
const knowledge_hub_1 = require("../providers/knowledge-hub");
|
|
9
10
|
async function configureKnowledge(_context, options = {}) {
|
|
10
11
|
const client = options.client || new knowledge_hub_1.KnowledgeHubClient();
|
|
12
|
+
// 1. Ask Knowledge source / mode
|
|
11
13
|
const { source } = await inquirer_1.default.prompt({
|
|
12
14
|
type: 'list',
|
|
13
15
|
name: 'source',
|
|
14
|
-
message: '
|
|
16
|
+
message: 'Select knowledge configuration:',
|
|
15
17
|
choices: [
|
|
16
|
-
{ name: 'E Knowledge Hub
|
|
18
|
+
{ name: 'E Knowledge Hub / Portable Pack (with Life Database)', value: 'e-hub' },
|
|
19
|
+
{ name: 'Life Database Only (SQLite personal state, schedule, preferences)', value: 'life-only' },
|
|
17
20
|
{ name: 'Do not use knowledge', value: 'none' },
|
|
18
21
|
],
|
|
19
22
|
});
|
|
@@ -21,80 +24,165 @@ async function configureKnowledge(_context, options = {}) {
|
|
|
21
24
|
return {
|
|
22
25
|
config: {
|
|
23
26
|
provider: 'none',
|
|
27
|
+
lifeDatabase: false,
|
|
24
28
|
},
|
|
25
29
|
summary: {
|
|
26
30
|
Source: 'Do not use knowledge',
|
|
31
|
+
Knowledge: 'Disabled',
|
|
27
32
|
},
|
|
28
33
|
};
|
|
29
34
|
}
|
|
30
|
-
|
|
35
|
+
const summary = {};
|
|
36
|
+
const config = {
|
|
37
|
+
dbPath: 'siduri.sqlite',
|
|
38
|
+
lifeDatabase: true,
|
|
39
|
+
};
|
|
40
|
+
if (source === 'life-only') {
|
|
41
|
+
config.provider = 'unified';
|
|
42
|
+
summary.Source = 'Life Database';
|
|
43
|
+
summary['Life Database'] = 'Enabled (siduri.sqlite)';
|
|
44
|
+
summary['E-Pack'] = 'None';
|
|
45
|
+
return { config, summary };
|
|
46
|
+
}
|
|
47
|
+
// 2. Discovery-First E-Knowledge Resolution Flow
|
|
31
48
|
while (true) {
|
|
32
|
-
const
|
|
49
|
+
const promptRes = await inquirer_1.default.prompt({
|
|
33
50
|
type: 'input',
|
|
34
51
|
name: 'packId',
|
|
35
|
-
message: '
|
|
52
|
+
message: 'Enter package ID (@publisher/name), local pack path, or remote URL:',
|
|
36
53
|
default: '@vxnus/e-teyvat',
|
|
37
|
-
validate: (val) => val.trim().length > 0 || 'Please enter a package ID.',
|
|
54
|
+
validate: (val) => val.trim().length > 0 || 'Please enter a package ID, path, or URL.',
|
|
38
55
|
});
|
|
39
|
-
const
|
|
56
|
+
const target = (promptRes.packId || promptRes.query || '').trim();
|
|
57
|
+
// Case A: Remote HTTP endpoint
|
|
58
|
+
if (target.startsWith('http://') || target.startsWith('https://')) {
|
|
59
|
+
console.log(`\n\x1b[36m── Knowledge Provider ────────────────────────\x1b[0m\n`);
|
|
60
|
+
console.log(` \x1b[2mType:\x1b[0m Remote E-Provider`);
|
|
61
|
+
console.log(` \x1b[2mEndpoint:\x1b[0m ${target}`);
|
|
62
|
+
console.log(` \x1b[2mConnectors:\x1b[0m Remote only (HTTP stream)\n`);
|
|
63
|
+
config.provider = 'e-remote';
|
|
64
|
+
config.baseUrl = target;
|
|
65
|
+
config.pack = {
|
|
66
|
+
mode: 'remote',
|
|
67
|
+
baseUrl: target,
|
|
68
|
+
};
|
|
69
|
+
summary.Source = 'Remote E-Provider';
|
|
70
|
+
summary.Provider = target;
|
|
71
|
+
summary['E-Pack'] = `Remote (${target})`;
|
|
72
|
+
return { config, summary };
|
|
73
|
+
}
|
|
74
|
+
// Case B: Local path / file
|
|
75
|
+
if (target.startsWith('./') || target.startsWith('/') || node_fs_1.default.existsSync(target)) {
|
|
76
|
+
console.log(`\n\x1b[36m── Knowledge Provider ────────────────────────\x1b[0m\n`);
|
|
77
|
+
console.log(` \x1b[2mType:\x1b[0m Local Knowledge Pack`);
|
|
78
|
+
console.log(` \x1b[2mPath:\x1b[0m ${target}`);
|
|
79
|
+
console.log(` \x1b[2mConnectors:\x1b[0m Local only (Filesystem)\n`);
|
|
80
|
+
config.provider = 'e-knowledge';
|
|
81
|
+
config.packPath = target;
|
|
82
|
+
config.pack = {
|
|
83
|
+
mode: 'local',
|
|
84
|
+
packPath: target,
|
|
85
|
+
};
|
|
86
|
+
summary.Source = 'Local Knowledge Pack';
|
|
87
|
+
summary.Provider = target;
|
|
88
|
+
summary['E-Pack'] = `Local (${target})`;
|
|
89
|
+
return { config, summary };
|
|
90
|
+
}
|
|
91
|
+
// Case C: E Knowledge Hub package ID (@publisher/name)
|
|
40
92
|
let manifest;
|
|
41
93
|
try {
|
|
42
|
-
process.stdout.write('\
|
|
43
|
-
manifest = await client.resolveProvider(
|
|
44
|
-
process.stdout.write('\r\
|
|
94
|
+
process.stdout.write('\x1b[2mResolving knowledge metadata...\x1b[0m');
|
|
95
|
+
manifest = await client.resolveProvider(target);
|
|
96
|
+
process.stdout.write('\r\x1b[32m✓\x1b[0m Knowledge metadata resolved\n');
|
|
45
97
|
}
|
|
46
98
|
catch (err) {
|
|
47
|
-
process.stdout.write('\r\
|
|
48
|
-
console.log(`\
|
|
99
|
+
process.stdout.write('\r\x1b[33m!\x1b[0m Knowledge resolution failed\n');
|
|
100
|
+
console.log(`\x1b[33mReason:\x1b[0m ${err.message}\n`);
|
|
49
101
|
const { failureAction } = await inquirer_1.default.prompt({
|
|
50
102
|
type: 'list',
|
|
51
103
|
name: 'failureAction',
|
|
52
104
|
message: 'What would you like to do?',
|
|
53
105
|
choices: [
|
|
54
|
-
{ name: 'Try again /
|
|
55
|
-
{ name: '
|
|
106
|
+
{ name: 'Try again / Enter another package ID or path', value: 'retry' },
|
|
107
|
+
{ name: 'Skip attaching E-pack (keep Life DB only)', value: 'skip' },
|
|
56
108
|
{ name: 'Cancel', value: 'cancel' },
|
|
57
109
|
],
|
|
58
110
|
});
|
|
59
|
-
if (failureAction === 'retry')
|
|
111
|
+
if (failureAction === 'retry')
|
|
60
112
|
continue;
|
|
61
|
-
}
|
|
62
113
|
if (failureAction === 'skip') {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
};
|
|
114
|
+
config.provider = 'unified';
|
|
115
|
+
summary.Source = 'Life Database';
|
|
116
|
+
summary['E-Pack'] = 'None';
|
|
117
|
+
return { config, summary };
|
|
67
118
|
}
|
|
68
119
|
throw new Error('Knowledge configuration cancelled.');
|
|
69
120
|
}
|
|
70
|
-
// Display
|
|
71
|
-
(0, knowledge_hub_1.displayKnowledgeProviderSummary)(manifest,
|
|
72
|
-
|
|
121
|
+
// Display metadata
|
|
122
|
+
(0, knowledge_hub_1.displayKnowledgeProviderSummary)(manifest, target);
|
|
123
|
+
// Inspect supported connectors
|
|
124
|
+
const hasRemoteUrl = Boolean(manifest.distribution?.url);
|
|
125
|
+
const connectorChoices = [];
|
|
126
|
+
if (hasRemoteUrl) {
|
|
127
|
+
connectorChoices.push({
|
|
128
|
+
name: `Remote (Stream directly from ${manifest.distribution?.url} without local storage)`,
|
|
129
|
+
value: 'remote',
|
|
130
|
+
});
|
|
131
|
+
connectorChoices.push({
|
|
132
|
+
name: 'Local (Download and run offline pack files on this machine)',
|
|
133
|
+
value: 'local',
|
|
134
|
+
});
|
|
135
|
+
connectorChoices.push({
|
|
136
|
+
name: 'Hybrid (Local pack cache with remote provider fallback/sync)',
|
|
137
|
+
value: 'hybrid',
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
connectorChoices.push({
|
|
142
|
+
name: 'Local (Offline pack files on this machine)',
|
|
143
|
+
value: 'local',
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
connectorChoices.push({
|
|
147
|
+
name: 'Choose another provider',
|
|
148
|
+
value: 'retry',
|
|
149
|
+
});
|
|
150
|
+
const choiceRes = await inquirer_1.default.prompt({
|
|
73
151
|
type: 'list',
|
|
74
|
-
name: '
|
|
75
|
-
message: '
|
|
76
|
-
choices:
|
|
77
|
-
{ name: 'Yes, use this provider', value: 'yes' },
|
|
78
|
-
{ name: 'Choose another provider', value: 'retry' },
|
|
79
|
-
],
|
|
152
|
+
name: 'connectorMode',
|
|
153
|
+
message: 'Choose connection type for this pack:',
|
|
154
|
+
choices: connectorChoices,
|
|
80
155
|
});
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
provider: 'e-hub',
|
|
85
|
-
registryUrl: 'https://e.vxnus.xyz/api/v1/knowledge',
|
|
86
|
-
packId: trimmedPackId,
|
|
87
|
-
},
|
|
88
|
-
summary: {
|
|
89
|
-
Source: 'E Knowledge Hub',
|
|
90
|
-
Provider: manifest.displayName || manifest.name,
|
|
91
|
-
Package: trimmedPackId,
|
|
92
|
-
Version: manifest.version,
|
|
93
|
-
},
|
|
94
|
-
metadata: {
|
|
95
|
-
manifest,
|
|
96
|
-
},
|
|
97
|
-
};
|
|
156
|
+
const rawChoice = choiceRes.connectorMode ?? choiceRes.confirmProvider;
|
|
157
|
+
if (rawChoice === 'retry') {
|
|
158
|
+
continue;
|
|
98
159
|
}
|
|
160
|
+
const connectorMode = (rawChoice === 'yes'
|
|
161
|
+
? (hasRemoteUrl ? 'remote' : 'local')
|
|
162
|
+
: (['remote', 'local', 'hybrid'].includes(rawChoice || '') ? rawChoice : 'local'));
|
|
163
|
+
config.provider = 'e-hub';
|
|
164
|
+
config.registryUrl = 'https://e.vxnus.xyz/api/v1/knowledge';
|
|
165
|
+
config.packId = target;
|
|
166
|
+
config.pack = {
|
|
167
|
+
mode: connectorMode,
|
|
168
|
+
packId: target,
|
|
169
|
+
registryUrl: 'https://e.vxnus.xyz/api/v1/knowledge',
|
|
170
|
+
baseUrl: manifest.distribution?.url,
|
|
171
|
+
preferredMode: 'lexical',
|
|
172
|
+
};
|
|
173
|
+
if (manifest.distribution?.url) {
|
|
174
|
+
config.baseUrl = manifest.distribution.url;
|
|
175
|
+
}
|
|
176
|
+
summary.Source = 'E Knowledge Hub';
|
|
177
|
+
summary.Provider = manifest.displayName || manifest.name;
|
|
178
|
+
summary.Package = target;
|
|
179
|
+
summary.Version = manifest.version;
|
|
180
|
+
summary['Life Database'] = 'Enabled (siduri.sqlite)';
|
|
181
|
+
summary['E-Pack'] = `${manifest.displayName || manifest.name} (${connectorMode})`;
|
|
182
|
+
return {
|
|
183
|
+
config,
|
|
184
|
+
summary,
|
|
185
|
+
metadata: { manifest },
|
|
186
|
+
};
|
|
99
187
|
}
|
|
100
188
|
}
|
|
@@ -6,39 +6,21 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.configureMemory = configureMemory;
|
|
7
7
|
const inquirer_1 = __importDefault(require("inquirer"));
|
|
8
8
|
async function configureMemory(_context) {
|
|
9
|
-
const {
|
|
9
|
+
const { provider } = await inquirer_1.default.prompt({
|
|
10
10
|
type: 'list',
|
|
11
|
-
name: '
|
|
12
|
-
message: 'Memory
|
|
11
|
+
name: 'provider',
|
|
12
|
+
message: 'Memory storage engine?',
|
|
13
13
|
choices: [
|
|
14
|
-
{ name: '
|
|
14
|
+
{ name: 'SQLite (Unified zero-config WAL + FTS5 full-text indexing)', value: 'sqlite' },
|
|
15
15
|
],
|
|
16
16
|
});
|
|
17
|
-
const { deployment } = await inquirer_1.default.prompt({
|
|
18
|
-
type: 'list',
|
|
19
|
-
name: 'deployment',
|
|
20
|
-
message: 'PostgreSQL deployment target?',
|
|
21
|
-
choices: [
|
|
22
|
-
{ name: 'Supabase (Hosted Postgres with connection pooling)', value: 'supabase' },
|
|
23
|
-
{ name: 'Neon (Serverless Postgres with branch-per-companion)', value: 'neon' },
|
|
24
|
-
{ name: 'Local PostgreSQL (System service or local installation)', value: 'local' },
|
|
25
|
-
{ name: 'Other PostgreSQL URL', value: 'other' },
|
|
26
|
-
],
|
|
27
|
-
});
|
|
28
|
-
const deploymentDisplayNames = {
|
|
29
|
-
supabase: 'Supabase',
|
|
30
|
-
neon: 'Neon',
|
|
31
|
-
local: 'Local PostgreSQL',
|
|
32
|
-
other: 'Other PostgreSQL',
|
|
33
|
-
};
|
|
34
17
|
return {
|
|
35
18
|
config: {
|
|
36
|
-
provider
|
|
37
|
-
deployment,
|
|
19
|
+
provider,
|
|
38
20
|
},
|
|
39
21
|
summary: {
|
|
40
|
-
Database: '
|
|
41
|
-
|
|
22
|
+
Database: 'SQLite (WAL + FTS5)',
|
|
23
|
+
Storage: 'siduri.sqlite',
|
|
42
24
|
},
|
|
43
25
|
};
|
|
44
26
|
}
|
|
@@ -179,14 +179,13 @@ describe('Guided Manifest-Driven Configuration UX Specification Tests', () => {
|
|
|
179
179
|
});
|
|
180
180
|
});
|
|
181
181
|
describe('Other Organ Configurators & Generic Router', () => {
|
|
182
|
-
test('Memory configurator configures
|
|
182
|
+
test('Memory configurator configures SQLite with WAL and FTS5', async () => {
|
|
183
183
|
inquirer_1.default.prompt
|
|
184
|
-
.mockResolvedValueOnce({
|
|
185
|
-
.mockResolvedValueOnce({ deployment: 'supabase' });
|
|
184
|
+
.mockResolvedValueOnce({ provider: 'sqlite' });
|
|
186
185
|
const result = await (0, memory_1.configureMemory)({ companionName: 'Sparkle', manifest: memoryManifest });
|
|
187
|
-
expect(result.config.provider).toBe('
|
|
188
|
-
expect(result.
|
|
189
|
-
expect(result.summary?.
|
|
186
|
+
expect(result.config.provider).toBe('sqlite');
|
|
187
|
+
expect(result.summary?.Database).toBe('SQLite (WAL + FTS5)');
|
|
188
|
+
expect(result.summary?.Storage).toBe('siduri.sqlite');
|
|
190
189
|
});
|
|
191
190
|
test('Voice configurator configures VOICEVOX engine and speaker ID', async () => {
|
|
192
191
|
inquirer_1.default.prompt
|
|
@@ -283,7 +282,7 @@ describe('Guided Manifest-Driven Configuration UX Specification Tests', () => {
|
|
|
283
282
|
test('formatReviewSummary displays actual configuration values and metadata', () => {
|
|
284
283
|
const summaryOutput = (0, index_1.formatReviewSummary)('Sparkle', [brainManifest, memoryManifest, knowledgeManifest, voiceManifest], {
|
|
285
284
|
brain: { Provider: 'OpenRouter', Model: 'openai/gpt-4o-mini' },
|
|
286
|
-
memory: { Database: '
|
|
285
|
+
memory: { Database: 'SQLite (WAL + FTS5)', Storage: 'siduri.sqlite' },
|
|
287
286
|
knowledge: { Source: 'E Knowledge Hub', Provider: 'E Teyvat', Package: '@vxnus/e-teyvat', Version: '1.2.0' },
|
|
288
287
|
voice: { Provider: 'VOICEVOX', 'Speaker ID': 1 },
|
|
289
288
|
});
|
|
@@ -292,8 +291,8 @@ describe('Guided Manifest-Driven Configuration UX Specification Tests', () => {
|
|
|
292
291
|
expect(summaryOutput).toContain('OpenRouter');
|
|
293
292
|
expect(summaryOutput).toContain('openai/gpt-4o-mini');
|
|
294
293
|
expect(summaryOutput).toContain('Memory');
|
|
295
|
-
expect(summaryOutput).toContain('
|
|
296
|
-
expect(summaryOutput).toContain('
|
|
294
|
+
expect(summaryOutput).toContain('SQLite');
|
|
295
|
+
expect(summaryOutput).toContain('siduri.sqlite');
|
|
297
296
|
expect(summaryOutput).toContain('Knowledge');
|
|
298
297
|
expect(summaryOutput).toContain('E Knowledge Hub');
|
|
299
298
|
expect(summaryOutput).toContain('E Teyvat');
|
|
@@ -304,7 +303,7 @@ describe('Guided Manifest-Driven Configuration UX Specification Tests', () => {
|
|
|
304
303
|
test('generateInstanceFiles accurately embeds configured organ values in siduri.config.json', () => {
|
|
305
304
|
const organConfigs = {
|
|
306
305
|
brain: { provider: 'openrouter', model: 'openai/gpt-4o-mini', apiKeyEnv: 'OPENROUTER_API_KEY' },
|
|
307
|
-
memory: { provider: '
|
|
306
|
+
memory: { provider: 'sqlite' },
|
|
308
307
|
knowledge: { provider: 'e-hub', registryUrl: 'https://e.vxnus.xyz/api/v1/knowledge', packId: '@vxnus/e-teyvat' },
|
|
309
308
|
voice: { provider: 'voicevox', speakerId: 2, baseUrl: 'http://localhost:50021' },
|
|
310
309
|
};
|
|
@@ -317,14 +316,14 @@ describe('Guided Manifest-Driven Configuration UX Specification Tests', () => {
|
|
|
317
316
|
expect(config.name).toBe('Sparkle');
|
|
318
317
|
expect(config.organs.brain.provider).toBe('openrouter');
|
|
319
318
|
expect(config.organs.brain.model).toBe('openai/gpt-4o-mini');
|
|
320
|
-
expect(config.organs.memory.
|
|
319
|
+
expect(config.organs.memory.provider).toBe('sqlite');
|
|
321
320
|
expect(config.organs.knowledge.provider).toBe('e-hub');
|
|
322
321
|
expect(config.organs.knowledge.packId).toBe('@vxnus/e-teyvat');
|
|
323
322
|
expect(config.organs.voice.speakerId).toBe(2);
|
|
324
323
|
// Verify explicit imports in src/index.js
|
|
325
324
|
expect(files['src/index.js']).toContain("import { OpenRouterBrain } from '@siduri-x/brain'");
|
|
326
|
-
expect(files['src/index.js']).toContain("import {
|
|
327
|
-
expect(files['src/index.js']).toContain("import {
|
|
325
|
+
expect(files['src/index.js']).toContain("import { SqliteMemoryStore } from '@siduri-x/memory'");
|
|
326
|
+
expect(files['src/index.js']).toContain("import { UnifiedKnowledgeOrgan } from '@siduri-x/knowledge'");
|
|
328
327
|
expect(files['src/index.js']).toContain("import { VoiceAdapter } from '@siduri-x/voice'");
|
|
329
328
|
});
|
|
330
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');
|