@vxnus/siduri 2.0.0 → 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +7 -7
  2. package/dist/builtin-manifests.js +38 -32
  3. package/dist/clean-machine-e2e.test.js +16 -16
  4. package/dist/clean-machine-smoke.test.d.ts +1 -0
  5. package/dist/clean-machine-smoke.test.js +166 -0
  6. package/dist/configurators/knowledge.js +134 -46
  7. package/dist/configurators/memory.js +7 -25
  8. package/dist/configurators.test.js +11 -12
  9. package/dist/doctor.js +58 -14
  10. package/dist/generator.js +95 -19
  11. package/dist/generator.test.js +9 -7
  12. package/dist/index.d.ts +1 -1
  13. package/dist/index.js +37 -32
  14. package/dist/manifest.d.ts +1 -1
  15. package/dist/providers/openrouter.js +1 -1
  16. package/dist/release-check.js +1 -1
  17. package/dist/web-dist/404/index.html +1 -1
  18. package/dist/web-dist/404.html +1 -1
  19. package/dist/web-dist/__next.__PAGE__.txt +12 -12
  20. package/dist/web-dist/__next._full.txt +12 -12
  21. package/dist/web-dist/__next._tree.txt +1 -1
  22. package/dist/web-dist/_next/static/chunks/0jgw-jf26m0ui.js +22 -0
  23. package/dist/web-dist/_next/static/chunks/0tdcnq1-n6kof.js +31 -0
  24. package/dist/web-dist/_next/static/chunks/12vwhiuvdy_i7.js +1 -0
  25. package/dist/web-dist/_next/static/chunks/{2_duynll0m51m.js → 15zsrb5462n_-.js} +2 -2
  26. package/dist/web-dist/_next/static/chunks/2dsfgoa225hpl.js +1 -0
  27. package/dist/web-dist/_next/static/chunks/2mno6yfdoee0x.js +1 -0
  28. package/dist/web-dist/_next/static/chunks/30a48dk5bjyhj.js +1 -0
  29. package/dist/web-dist/_next/static/chunks/360luxuz2x8y_.js +1 -0
  30. package/dist/web-dist/_next/static/chunks/turbopack-2hpygfb68u21h.js +1 -0
  31. package/dist/web-dist/_not-found/__next._full.txt +8 -8
  32. package/dist/web-dist/_not-found/__next._not-found.__PAGE__.txt +7 -7
  33. package/dist/web-dist/_not-found/__next._tree.txt +1 -1
  34. package/dist/web-dist/_not-found/index.html +1 -1
  35. package/dist/web-dist/_not-found/index.txt +8 -8
  36. package/dist/web-dist/chat/__next._full.txt +9 -9
  37. package/dist/web-dist/chat/__next._tree.txt +1 -1
  38. package/dist/web-dist/chat/__next.chat.__PAGE__.txt +8 -8
  39. package/dist/web-dist/chat/index.html +1 -1
  40. package/dist/web-dist/chat/index.txt +9 -9
  41. package/dist/web-dist/index.html +1 -1
  42. package/dist/web-dist/index.txt +12 -12
  43. package/dist/web-dist/operator/__next._full.txt +9 -9
  44. package/dist/web-dist/operator/__next._tree.txt +1 -1
  45. package/dist/web-dist/operator/__next.operator.__PAGE__.txt +8 -8
  46. package/dist/web-dist/operator/index.html +1 -1
  47. package/dist/web-dist/operator/index.txt +9 -9
  48. package/dist/web-template.js +1 -1
  49. package/package.json +10 -10
  50. package/dist/web-dist/_next/static/chunks/09y7khbmco_v4.js +0 -1
  51. package/dist/web-dist/_next/static/chunks/0uwih7sydv413.js +0 -1
  52. package/dist/web-dist/_next/static/chunks/1_yae15ffa5f6.js +0 -1
  53. package/dist/web-dist/_next/static/chunks/2kcqevsmv6kh2.js +0 -31
  54. package/dist/web-dist/_next/static/chunks/2s3d1bfr0l4b0.js +0 -1
  55. package/dist/web-dist/_next/static/chunks/32z87id9k6i2z.js +0 -22
  56. package/dist/web-dist/_next/static/chunks/3bx2gs3idjmo1.js +0 -1
  57. package/dist/web-dist/_next/static/chunks/turbopack-3eipe4c-imn5l.js +0 -1
  58. /package/dist/web-dist/_next/static/{pWHUixFDDvANZeAf-shqT → gZ_ly5hNtyqFVZ9AuHs_A}/_buildManifest.js +0 -0
  59. /package/dist/web-dist/_next/static/{pWHUixFDDvANZeAf-shqT → gZ_ly5hNtyqFVZ9AuHs_A}/_clientMiddlewareManifest.js +0 -0
  60. /package/dist/web-dist/_next/static/{pWHUixFDDvANZeAf-shqT → gZ_ly5hNtyqFVZ9AuHs_A}/_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: 'Knowledge source?',
16
+ message: 'Select knowledge configuration:',
15
17
  choices: [
16
- { name: 'E Knowledge Hub (Search and attach remote factual pack)', value: 'e-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
- // E Knowledge Hub flow
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 { packId } = await inquirer_1.default.prompt({
49
+ const promptRes = await inquirer_1.default.prompt({
33
50
  type: 'input',
34
51
  name: 'packId',
35
- message: 'Search E Knowledge Hub or enter package ID:',
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 trimmedPackId = packId.trim();
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('\u001b[2mSearching E Knowledge Hub...\u001b[0m');
43
- manifest = await client.resolveProvider(trimmedPackId);
44
- process.stdout.write('\r\u001b[32m✓\u001b[0m Checking provider manifest\n');
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\u001b[33m!\u001b[0m Provider resolution failed\n');
48
- console.log(`\u001b[33mReason:\u001b[0m ${err.message}\n`);
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 / Search another provider', value: 'retry' },
55
- { name: 'Do not use knowledge', value: 'skip' },
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
- return {
64
- config: { provider: 'none' },
65
- summary: { Source: 'Do not use knowledge' },
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 provider summary
71
- (0, knowledge_hub_1.displayKnowledgeProviderSummary)(manifest, trimmedPackId);
72
- const { confirmProvider } = await inquirer_1.default.prompt({
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: 'confirmProvider',
75
- message: 'Use this knowledge provider?',
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
- if (confirmProvider === 'yes') {
82
- return {
83
- config: {
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 { database } = await inquirer_1.default.prompt({
9
+ const { provider } = await inquirer_1.default.prompt({
10
10
  type: 'list',
11
- name: 'database',
12
- message: 'Memory database?',
11
+ name: 'provider',
12
+ message: 'Memory storage engine?',
13
13
  choices: [
14
- { name: 'PostgreSQL (FTS relational claims with ACID durability)', value: 'postgres' },
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: database,
37
- deployment,
19
+ provider,
38
20
  },
39
21
  summary: {
40
- Database: 'PostgreSQL',
41
- Deploy: deploymentDisplayNames[deployment] || deployment,
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 PostgreSQL with Supabase deployment', async () => {
182
+ test('Memory configurator configures SQLite with WAL and FTS5', async () => {
183
183
  inquirer_1.default.prompt
184
- .mockResolvedValueOnce({ database: 'postgres' })
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('postgres');
188
- expect(result.config.deployment).toBe('supabase');
189
- expect(result.summary?.Deploy).toBe('Supabase');
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: 'PostgreSQL', Deploy: 'Supabase' },
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('PostgreSQL');
296
- expect(summaryOutput).toContain('Supabase');
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: 'postgres', deployment: 'supabase' },
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.deployment).toBe('supabase');
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
325
  expect(files['src/index.js']).toContain("import { SqliteMemoryStore } from '@siduri-x/memory'");
327
- expect(files['src/index.js']).toContain("import { EKnowledgeAdapter } from '@siduri-x/knowledge'");
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/doctor.js CHANGED
@@ -129,25 +129,69 @@ async function runDoctor(options = {}) {
129
129
  // 3. Check External Services
130
130
  for (const m of selectedManifests) {
131
131
  for (const service of m.services || []) {
132
- results.push({
133
- category: 'Services',
134
- name: `${service.name} (${m.organType})`,
135
- status: 'PASS',
136
- organName: m.name,
137
- message: 'Service requirement declared',
138
- });
132
+ const organConf = configuredOrgansMap[m.configKey] || configuredOrgansMap[m.organType];
133
+ const configuredUrl = organConf?.baseUrl || organConf?.url || effectiveEnv[`${service.name.toUpperCase()}_URL`];
134
+ if (configuredUrl && typeof configuredUrl === 'string' && configuredUrl.startsWith('http')) {
135
+ try {
136
+ const controller = new AbortController();
137
+ const timer = setTimeout(() => controller.abort(), 1000);
138
+ await fetch(configuredUrl, { signal: controller.signal }).catch(() => { });
139
+ clearTimeout(timer);
140
+ results.push({
141
+ category: 'Services',
142
+ name: `${service.name} (${m.organType})`,
143
+ status: 'PASS',
144
+ organName: m.name,
145
+ message: `Endpoint configured: ${configuredUrl}`,
146
+ });
147
+ }
148
+ catch {
149
+ results.push({
150
+ category: 'Services',
151
+ name: `${service.name} (${m.organType})`,
152
+ status: 'OPTIONAL_MISSING',
153
+ organName: m.name,
154
+ message: `Service at ${configuredUrl} is not responding (optional local service)`,
155
+ remediation: `Start ${service.name} or allow auto-download if applicable.`,
156
+ });
157
+ }
158
+ }
159
+ else {
160
+ results.push({
161
+ category: 'Services',
162
+ name: `${service.name} (${m.organType})`,
163
+ status: 'PASS',
164
+ organName: m.name,
165
+ message: 'Service requirement declared (managed locally)',
166
+ });
167
+ }
139
168
  }
140
169
  }
141
170
  // 4. Database Check (only if an organ declares database requirement)
142
171
  const dbOrgan = selectedManifests.find((m) => m.database !== null && m.database !== undefined);
143
172
  if (dbOrgan) {
144
- results.push({
145
- category: 'Database',
146
- name: 'SQLite Database',
147
- status: 'PASS',
148
- organName: dbOrgan.name,
149
- message: 'SQLite operates natively.',
150
- });
173
+ try {
174
+ const testDbPath = node_path_1.default.join(projectDir, '.doctor-sqlite-test.tmp');
175
+ node_fs_1.default.writeFileSync(testDbPath, '');
176
+ node_fs_1.default.unlinkSync(testDbPath);
177
+ results.push({
178
+ category: 'Database',
179
+ name: 'SQLite Database',
180
+ status: 'PASS',
181
+ organName: dbOrgan.name,
182
+ message: 'Local storage directory writeable; SQLite operational',
183
+ });
184
+ }
185
+ catch (err) {
186
+ results.push({
187
+ category: 'Database',
188
+ name: 'SQLite Database',
189
+ status: 'FAIL',
190
+ organName: dbOrgan.name,
191
+ message: `Storage directory write check failed: ${err.message}`,
192
+ remediation: 'Verify write permissions for the companion directory.',
193
+ });
194
+ }
151
195
  }
152
196
  else {
153
197
  results.push({
package/dist/generator.js CHANGED
@@ -36,7 +36,7 @@ function getDefaultConfigForManifest(manifest) {
36
36
  config.apiKeyEnv = 'OPENROUTER_API_KEY';
37
37
  }
38
38
  else if (manifest.organType === 'memory') {
39
- config.provider = config.provider || 'postgres';
39
+ config.provider = config.provider || 'sqlite';
40
40
  }
41
41
  else if (manifest.organType === 'voice') {
42
42
  config.provider = config.provider || 'voicevox';
@@ -72,7 +72,7 @@ function getDefaultConfigForManifest(manifest) {
72
72
  function generateInstanceFiles(options) {
73
73
  const instanceName = options.name || 'my-siduri';
74
74
  const instanceId = options.id || 'default';
75
- const coreVersion = options.coreVersion || '^1.0.9';
75
+ const coreVersion = options.coreVersion || '^2.0.2';
76
76
  const manifests = options.selectedManifests;
77
77
  const hasMemory = manifests.some((m) => m.organType === 'memory');
78
78
  const hasVoice = manifests.some((m) => m.organType === 'voice');
@@ -80,14 +80,12 @@ function generateInstanceFiles(options) {
80
80
  const hasMouth = manifests.some((m) => m.organType === 'mouth');
81
81
  const voiceConfig = options.organConfigs?.voice || options.organConfigs?.['@siduri-x/voice'];
82
82
  const isVoicevox = hasVoice && (!voiceConfig || voiceConfig.provider === 'voicevox');
83
- const memoryConfig = options.organConfigs?.memory || options.organConfigs?.['@siduri-x/memory'];
84
- const isPostgresLocal = hasMemory && (!memoryConfig || memoryConfig.deployment === 'local' || memoryConfig.provider === 'postgres');
85
83
  // 1. package.json
86
84
  const dependencies = {
87
85
  '@siduri-x/core': coreVersion,
88
86
  };
89
87
  for (const m of manifests) {
90
- dependencies[m.name] = `^${m.version || '1.0.1'}`;
88
+ dependencies[m.name] = `^${m.version || '2.0.1'}`;
91
89
  }
92
90
  const scripts = {
93
91
  start: 'node src/index.js',
@@ -95,12 +93,19 @@ function generateInstanceFiles(options) {
95
93
  doctor: 'siduri doctor',
96
94
  db: 'siduri db',
97
95
  };
96
+ const devDependencies = {
97
+ '@vxnus/siduri': coreVersion,
98
+ };
98
99
  const packageJsonObj = {
99
100
  name: instanceName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'my-siduri',
100
101
  private: true,
101
102
  type: 'module',
103
+ engines: {
104
+ node: '>=22.16.0',
105
+ },
102
106
  scripts,
103
107
  dependencies,
108
+ devDependencies,
104
109
  };
105
110
  const packageJson = JSON.stringify(packageJsonObj, null, 2) + '\n';
106
111
  // 3. siduri.config.json
@@ -164,14 +169,35 @@ function generateInstanceFiles(options) {
164
169
  `import { SiduriRuntime, dispatchCompanionChat, validateCompanionConfig } from '@siduri-x/core';`,
165
170
  ];
166
171
  for (const m of manifests) {
167
- importLines.push(`import { ${m.factory} } from '${m.name}';`);
172
+ if (m.name === '@siduri-x/self') {
173
+ importLines.push(`import { ActiveSelfCompiler, SqliteSelfRepository } from '@siduri-x/self';`);
174
+ }
175
+ else {
176
+ importLines.push(`import { ${m.factory} } from '${m.name}';`);
177
+ }
168
178
  }
169
179
  const instantiationLines = [];
170
180
  const organMapEntries = [];
171
181
  for (const m of manifests) {
172
- const varName = m.configKey;
173
- instantiationLines.push(`const ${varName} = new ${m.factory}(config.organs.${m.configKey});`);
174
- organMapEntries.push(` ${varName},`);
182
+ if (m.name === '@siduri-x/self') {
183
+ instantiationLines.push(`const self = new SqliteSelfRepository({ dbPath: path.resolve(rootDir, 'siduri.sqlite') });`);
184
+ instantiationLines.push(`const behavior = new ActiveSelfCompiler(config.organs.behavior);`);
185
+ organMapEntries.push(` behavior,`);
186
+ organMapEntries.push(` self,`);
187
+ }
188
+ else if (m.name === '@siduri-x/memory') {
189
+ instantiationLines.push(`const memory = new ${m.factory}({ ...config.organs.${m.configKey}, dbPath: path.resolve(rootDir, config.organs.${m.configKey}?.dbPath || 'siduri.sqlite') });`);
190
+ organMapEntries.push(` ${m.configKey},`);
191
+ }
192
+ else if (m.name === '@siduri-x/knowledge') {
193
+ instantiationLines.push(`const knowledge = new ${m.factory}({ ...config.organs.${m.configKey}, dbPath: path.resolve(rootDir, config.organs.${m.configKey}?.dbPath || 'siduri.sqlite') });`);
194
+ organMapEntries.push(` ${m.configKey},`);
195
+ }
196
+ else {
197
+ const varName = m.configKey;
198
+ instantiationLines.push(`const ${varName} = new ${m.factory}(config.organs.${m.configKey});`);
199
+ organMapEntries.push(` ${varName},`);
200
+ }
175
201
  }
176
202
  if (hasMouth && hasVoice) {
177
203
  instantiationLines.push(`if (typeof mouth?.setVoiceOrgan === 'function') mouth.setVoiceOrgan(voice);`);
@@ -184,6 +210,14 @@ function generateInstanceFiles(options) {
184
210
  `const __dirname = path.dirname(__filename);`,
185
211
  `const rootDir = path.resolve(__dirname, '..');`,
186
212
  '',
213
+ `try {`,
214
+ ` process.loadEnvFile(path.join(rootDir, '.env'));`,
215
+ `} catch (err) {`,
216
+ ` if (err?.code !== 'ENOENT') {`,
217
+ ` console.error('Failed to load .env file:', err.message);`,
218
+ ` }`,
219
+ `}`,
220
+ '',
187
221
  `const config = JSON.parse(`,
188
222
  ` await readFile(path.join(rootDir, 'siduri.config.json'), 'utf8')`,
189
223
  `);`,
@@ -222,6 +256,51 @@ function generateInstanceFiles(options) {
222
256
  ` return;`,
223
257
  ` }`,
224
258
  '',
259
+ ` // API: Ready probe`,
260
+ ` if (pathname === '/ready' && req.method === 'GET') {`,
261
+ ` res.writeHead(200, { 'Content-Type': 'application/json' });`,
262
+ ` res.end(JSON.stringify({ status: 'ready', companionId: config.id }));`,
263
+ ` return;`,
264
+ ` }`,
265
+ '',
266
+ ` // API: Version info`,
267
+ ` if (pathname === '/version' && req.method === 'GET') {`,
268
+ ` res.writeHead(200, { 'Content-Type': 'application/json' });`,
269
+ ` res.end(JSON.stringify({ version: '2.0.2', name: config.name, id: config.id }));`,
270
+ ` return;`,
271
+ ` }`,
272
+ '',
273
+ ` // API: Identity info`,
274
+ ` if (pathname === '/me' && req.method === 'GET') {`,
275
+ ` res.writeHead(200, { 'Content-Type': 'application/json' });`,
276
+ ` res.end(JSON.stringify({`,
277
+ ` id: config.id,`,
278
+ ` name: config.name,`,
279
+ ` organs: Object.keys(config.organs || {}),`,
280
+ ` }));`,
281
+ ` return;`,
282
+ ` }`,
283
+ '',
284
+ ` // API: Voice & Observation health probes`,
285
+ ` if (pathname === '/voice/health' && req.method === 'GET') {`,
286
+ ` res.writeHead(200, { 'Content-Type': 'application/json' });`,
287
+ ` res.end(JSON.stringify({ provider: config.organs?.voice?.provider || 'none', configured: Boolean(runtime.voice) }));`,
288
+ ` return;`,
289
+ ` }`,
290
+ '',
291
+ ` if (pathname === '/obs/health' && req.method === 'GET') {`,
292
+ ` res.writeHead(200, { 'Content-Type': 'application/json' });`,
293
+ ` res.end(JSON.stringify({ provider: config.organs?.vision?.provider || 'none', configured: Boolean(runtime.vision) }));`,
294
+ ` return;`,
295
+ ` }`,
296
+ '',
297
+ ` // API: Evidence & observations`,
298
+ ` if ((pathname === '/evidence' || pathname === '/observations') && req.method === 'GET') {`,
299
+ ` res.writeHead(200, { 'Content-Type': 'application/json' });`,
300
+ ` res.end(JSON.stringify({ items: [] }));`,
301
+ ` return;`,
302
+ ` }`,
303
+ '',
225
304
  ` // API: Audio buffer retrieval for voice playback`,
226
305
  ` if (pathname.startsWith('/api/audio/') && req.method === 'GET') {`,
227
306
  ` const audioId = pathname.slice('/api/audio/'.length);`,
@@ -354,8 +433,8 @@ function generateInstanceFiles(options) {
354
433
  ` const abortController = new AbortController();`,
355
434
  ` const onClose = () => {`,
356
435
  ` abortController.abort('client_disconnect');`,
357
- ` if (typeof runtime.interruptMouth === 'function') {`,
358
- ` runtime.interruptMouth('client_disconnect');`,
436
+ ` if (runtime.mouth && typeof runtime.mouth.interrupt === 'function') {`,
437
+ ` runtime.mouth.interrupt('client_disconnect');`,
359
438
  ` }`,
360
439
  ` };`,
361
440
  ` req.on('close', onClose);`,
@@ -430,8 +509,8 @@ function generateInstanceFiles(options) {
430
509
  ` try {`,
431
510
  ` const payload = JSON.parse(body || '{}');`,
432
511
  ` const reason = payload.reason || 'user_barge_in';`,
433
- ` if (typeof runtime.interruptMouth === 'function') {`,
434
- ` runtime.interruptMouth(reason);`,
512
+ ` if (runtime.mouth && typeof runtime.mouth.interrupt === 'function') {`,
513
+ ` runtime.mouth.interrupt(reason);`,
435
514
  ` }`,
436
515
  ` res.writeHead(200, { 'Content-Type': 'application/json' });`,
437
516
  ` res.end(JSON.stringify({ success: true, interrupted: true, reason }));`,
@@ -573,20 +652,17 @@ function generateInstanceFiles(options) {
573
652
  '',
574
653
  '## Prerequisites',
575
654
  '',
576
- '- **Node.js**: `v20.0.0` or higher',
655
+ '- **Node.js**: `v22.16.0` or higher',
577
656
  '- **Environment**: Valid `.env` file (configured from `.env.example`)',
578
657
  ];
579
658
  if (hasMemory) {
580
- readmeLines.push('- **PostgreSQL (Optional)**: Required if PostgreSQL memory organ is enabled (local PostgreSQL server or cloud Supabase/Neon).');
659
+ readmeLines.push('- **Database**: Embedded SQLite (`siduri.sqlite`). Automatically initialized with WAL mode and FTS5 full-text indexing with zero external setup.');
581
660
  }
582
661
  if (isVoicevox) {
583
662
  readmeLines.push('- **VOICEVOX**: Note: The Voicevox engine executable will be securely auto-downloaded at runtime by Siduri if no local URL is provided.');
584
663
  }
585
664
  readmeLines.push('', '## Getting Started', '', '### 1. Install Dependencies', '```bash', 'npm install', '```', '', '### 2. Configure Environment', '```bash', 'cp .env.example .env', '```', 'Fill in your LLM API key (e.g. `OPENROUTER_API_KEY`) and any other service credentials in `.env`.');
586
- if (hasMemory) {
587
- readmeLines.push('', '### 3. Database Migrations', 'Ensure `DATABASE_URL` in `.env` is reachable, then push the memory organ PostgreSQL schema:', '```bash', 'npx @vxnus/siduri db push', '```');
588
- }
589
- readmeLines.push('', `### ${hasMemory ? '4' : '3'}. Diagnostics & Health Probe`, 'Verify all environment variables, schema conformance, services, and organ connections:', '```bash', 'npm run doctor', '```', '', `### ${hasMemory ? '5' : '4'}. Start Companion & Web Console`, 'Launch your companion runtime and Web UI / Memory Control Panel:', '```bash', 'npm start', '```', 'Then open `http://localhost:3000` in your browser.');
665
+ readmeLines.push('', '### 3. Diagnostics & Health Probe', 'Verify all environment variables, schema conformance, services, and organ connections:', '```bash', 'npm run doctor', '```', '', '### 4. Start Companion & Web Console', 'Launch your companion runtime and Web UI / Memory Control Panel:', '```bash', 'npm start', '```', 'Then open `http://localhost:3000` in your browser.');
590
666
  const companionSlug = instanceName.toLowerCase().replace(/[^a-z0-9_-]/g, '') || 'default';
591
667
  const createAssetsDirs = [];
592
668
  if (hasBody) {