@vxnus/siduri 2.0.2 → 2.0.4

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 (39) hide show
  1. package/dist/builtin-manifests.js +8 -8
  2. package/dist/configurators/knowledge.js +81 -125
  3. package/dist/configurators.test.js +41 -8
  4. package/dist/generator.js +11 -3
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.js +57 -30
  7. package/dist/providers/knowledge-hub.d.ts +8 -1
  8. package/dist/providers/knowledge-hub.js +21 -0
  9. package/dist/web-dist/404/index.html +1 -1
  10. package/dist/web-dist/404.html +1 -1
  11. package/dist/web-dist/__next.__PAGE__.txt +3 -3
  12. package/dist/web-dist/__next._full.txt +3 -3
  13. package/dist/web-dist/__next._tree.txt +2 -2
  14. package/dist/web-dist/_next/static/chunks/0e0_fwxai4qpl.css +1 -0
  15. package/dist/web-dist/_next/static/chunks/1vumcan23qv3j.js +30 -0
  16. package/dist/web-dist/_next/static/chunks/24608ekqxlkf6.js +1 -0
  17. package/dist/web-dist/_not-found/__next._full.txt +2 -2
  18. package/dist/web-dist/_not-found/__next._not-found.__PAGE__.txt +2 -2
  19. package/dist/web-dist/_not-found/__next._tree.txt +2 -2
  20. package/dist/web-dist/_not-found/index.html +1 -1
  21. package/dist/web-dist/_not-found/index.txt +2 -2
  22. package/dist/web-dist/chat/__next._full.txt +3 -3
  23. package/dist/web-dist/chat/__next._tree.txt +2 -2
  24. package/dist/web-dist/chat/__next.chat.__PAGE__.txt +3 -3
  25. package/dist/web-dist/chat/index.html +1 -1
  26. package/dist/web-dist/chat/index.txt +3 -3
  27. package/dist/web-dist/index.html +1 -1
  28. package/dist/web-dist/index.txt +3 -3
  29. package/dist/web-dist/operator/__next._full.txt +2 -2
  30. package/dist/web-dist/operator/__next._tree.txt +2 -2
  31. package/dist/web-dist/operator/__next.operator.__PAGE__.txt +2 -2
  32. package/dist/web-dist/operator/index.html +1 -1
  33. package/dist/web-dist/operator/index.txt +2 -2
  34. package/package.json +1 -1
  35. package/dist/web-dist/_next/static/chunks/0xhe1hasjan-9.css +0 -1
  36. package/dist/web-dist/_next/static/chunks/15zsrb5462n_-.js +0 -30
  37. /package/dist/web-dist/_next/static/{gZ_ly5hNtyqFVZ9AuHs_A → AZPivdC8Bi_P27SqlyGnq}/_buildManifest.js +0 -0
  38. /package/dist/web-dist/_next/static/{gZ_ly5hNtyqFVZ9AuHs_A → AZPivdC8Bi_P27SqlyGnq}/_clientMiddlewareManifest.js +0 -0
  39. /package/dist/web-dist/_next/static/{gZ_ly5hNtyqFVZ9AuHs_A → AZPivdC8Bi_P27SqlyGnq}/_ssgManifest.js +0 -0
@@ -5,8 +5,8 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
5
5
  {
6
6
  name: '@siduri-x/self',
7
7
  organType: 'behavior',
8
- version: '2.0.1',
9
- displayName: 'Behavior / Self (Active Self Directives)',
8
+ version: '2.0.2',
9
+ displayName: 'Behavior & Self (Personality Directives)',
10
10
  description: 'Atomic directive state machine and personality projection compiler',
11
11
  entrypoint: './dist/index.js',
12
12
  factory: 'ActiveSelfCompiler',
@@ -207,8 +207,8 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
207
207
  {
208
208
  name: '@siduri-x/knowledge',
209
209
  organType: 'knowledge',
210
- version: '2.0.1',
211
- displayName: 'Knowledge (Life DB & Portable E-Packs)',
210
+ version: '2.0.2',
211
+ displayName: 'Knowledge (Sovereign Life DB & Knowledge Packs)',
212
212
  description: 'Sovereign Life Database (finances, inventory, schedule, preferences) and portable E knowledge packs',
213
213
  entrypoint: './dist/index.js',
214
214
  factory: 'UnifiedKnowledgeOrgan',
@@ -286,7 +286,7 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
286
286
  name: '@siduri-x/memory',
287
287
  organType: 'memory',
288
288
  version: '2.0.1',
289
- displayName: 'Memory (SQLite FTS5 Claims)',
289
+ displayName: 'Memory (Long-term Recall & State)',
290
290
  description: 'Relational semantic claims with Full-Text Search (FTS5) and companion isolation',
291
291
  entrypoint: './dist/index.js',
292
292
  factory: 'SqliteMemoryStore',
@@ -380,9 +380,9 @@ exports.BUILTIN_ORGAN_MANIFESTS = [
380
380
  {
381
381
  name: '@siduri-x/voice',
382
382
  organType: 'voice',
383
- version: '2.0.1',
384
- displayName: 'Voice (VOICEVOX Speech Synthesis)',
385
- description: 'Queued speech synthesis and audio rendering lifecycle adapter',
383
+ version: '2.0.2',
384
+ displayName: 'Voice (Speech Synthesis & Voice Conversion)',
385
+ description: 'Queued speech synthesis and audio rendering lifecycle adapter (Edge-TTS, Kokoro, Piper, VOICEVOX, RVC)',
386
386
  entrypoint: './dist/index.js',
387
387
  factory: 'VoicevoxAdapter',
388
388
  configKey: 'voice',
@@ -5,90 +5,38 @@ 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"));
9
8
  const knowledge_hub_1 = require("../providers/knowledge-hub");
10
9
  async function configureKnowledge(_context, options = {}) {
11
10
  const client = options.client || new knowledge_hub_1.KnowledgeHubClient();
12
- // 1. Ask Knowledge source / mode
13
- const { source } = await inquirer_1.default.prompt({
14
- type: 'list',
15
- name: 'source',
16
- message: 'Select knowledge configuration:',
17
- choices: [
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' },
20
- { name: 'Do not use knowledge', value: 'none' },
21
- ],
22
- });
23
- if (source === 'none') {
24
- return {
25
- config: {
26
- provider: 'none',
27
- lifeDatabase: false,
28
- },
29
- summary: {
30
- Source: 'Do not use knowledge',
31
- Knowledge: 'Disabled',
32
- },
33
- };
34
- }
35
- const summary = {};
36
11
  const config = {
12
+ provider: 'unified',
37
13
  dbPath: 'siduri.sqlite',
38
14
  lifeDatabase: true,
39
15
  };
40
- if (source === 'life-only') {
41
- config.provider = 'unified';
42
- summary.Source = 'Life Database';
43
- summary['Life Database'] = 'Enabled (siduri.sqlite)';
16
+ const summary = {
17
+ 'Life Database': 'Enabled (siduri.sqlite)',
18
+ };
19
+ // 1. Life Database is the sovereign default. Ask whether to attach an external knowledge pack
20
+ const { useExternalPack } = await inquirer_1.default.prompt({
21
+ type: 'confirm',
22
+ name: 'useExternalPack',
23
+ message: 'Attach an external knowledge pack from E Knowledge Hub?',
24
+ default: false,
25
+ });
26
+ if (!useExternalPack) {
44
27
  summary['E-Pack'] = 'None';
45
28
  return { config, summary };
46
29
  }
47
- // 2. Discovery-First E-Knowledge Resolution Flow
30
+ // 2. Discover and resolve E Knowledge Hub package
48
31
  while (true) {
49
32
  const promptRes = await inquirer_1.default.prompt({
50
33
  type: 'input',
51
34
  name: 'packId',
52
- message: 'Enter package ID (@publisher/name), local pack path, or remote URL:',
35
+ message: 'Enter package ID (@publisher/name):',
53
36
  default: '@vxnus/e-teyvat',
54
- validate: (val) => val.trim().length > 0 || 'Please enter a package ID, path, or URL.',
37
+ validate: (val) => val.trim().length > 0 || 'Please enter a package ID.',
55
38
  });
56
39
  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)
92
40
  let manifest;
93
41
  try {
94
42
  process.stdout.write('\x1b[2mResolving knowledge metadata...\x1b[0m');
@@ -103,82 +51,90 @@ async function configureKnowledge(_context, options = {}) {
103
51
  name: 'failureAction',
104
52
  message: 'What would you like to do?',
105
53
  choices: [
106
- { name: 'Try again / Enter another package ID or path', value: 'retry' },
107
- { name: 'Skip attaching E-pack (keep Life DB only)', value: 'skip' },
54
+ { name: 'Try again / Enter another package ID', value: 'retry' },
55
+ { name: 'Continue without external pack (Life DB only)', value: 'skip' },
108
56
  { name: 'Cancel', value: 'cancel' },
109
57
  ],
110
58
  });
111
59
  if (failureAction === 'retry')
112
60
  continue;
113
61
  if (failureAction === 'skip') {
114
- config.provider = 'unified';
115
- summary.Source = 'Life Database';
116
62
  summary['E-Pack'] = 'None';
117
63
  return { config, summary };
118
64
  }
119
65
  throw new Error('Knowledge configuration cancelled.');
120
66
  }
121
- // Display metadata
67
+ // Display provider summary
122
68
  (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',
69
+ // Determine connection type: User selects strictly 1 mode (Remote OR Local)
70
+ let connectionType;
71
+ const isHybrid = manifest.distributionType === 'both';
72
+ const isLocalOnly = manifest.distributionType === 'local' || manifest.distribution?.kind === 'archive';
73
+ if (isHybrid) {
74
+ const { selectedMode } = await inquirer_1.default.prompt({
75
+ type: 'list',
76
+ name: 'selectedMode',
77
+ message: 'This pack offers both remote and local options. Select connection type:',
78
+ choices: [
79
+ {
80
+ name: `Remote (Stream directly from ${manifest.distribution?.url || 'provider'} without local storage)`,
81
+ value: 'remote',
82
+ },
83
+ {
84
+ name: 'Local (Download archive and run offline on this machine)',
85
+ value: 'local',
86
+ },
87
+ ],
138
88
  });
89
+ connectionType = selectedMode;
90
+ }
91
+ else if (isLocalOnly) {
92
+ connectionType = 'local';
139
93
  }
140
94
  else {
141
- connectorChoices.push({
142
- name: 'Local (Offline pack files on this machine)',
143
- value: 'local',
144
- });
95
+ connectionType = 'remote';
145
96
  }
146
- connectorChoices.push({
147
- name: 'Choose another provider',
148
- value: 'retry',
149
- });
150
- const choiceRes = await inquirer_1.default.prompt({
151
- type: 'list',
152
- name: 'connectorMode',
153
- message: 'Choose connection type for this pack:',
154
- choices: connectorChoices,
155
- });
156
- const rawChoice = choiceRes.connectorMode ?? choiceRes.confirmProvider;
157
- if (rawChoice === 'retry') {
158
- continue;
97
+ if (connectionType === 'remote') {
98
+ config.provider = 'e-hub';
99
+ config.registryUrl = 'https://e.vxnus.xyz/api/v1/knowledge';
100
+ config.packId = target;
101
+ config.pack = {
102
+ mode: 'remote',
103
+ packId: target,
104
+ registryUrl: 'https://e.vxnus.xyz/api/v1/knowledge',
105
+ baseUrl: manifest.distribution?.url,
106
+ preferredMode: 'lexical',
107
+ };
108
+ if (manifest.distribution?.url) {
109
+ config.baseUrl = manifest.distribution.url;
110
+ }
111
+ summary.Source = 'E Knowledge Hub';
112
+ summary.Provider = manifest.displayName || manifest.name;
113
+ summary.Package = target;
114
+ summary.Version = manifest.version;
115
+ summary['E-Pack'] = `${manifest.displayName || manifest.name} (Remote)`;
159
116
  }
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;
117
+ else {
118
+ // Local archive: stored in our defined path inside companion assets (user doesn't type path)
119
+ const packSlug = target.replace(/^@/, '').replace(/[/_]/g, '-');
120
+ const packDir = `./assets/knowledge/${packSlug}`;
121
+ config.provider = 'e-knowledge';
122
+ config.packPath = packDir;
123
+ config.packId = target;
124
+ config.pack = {
125
+ mode: 'local',
126
+ packId: target,
127
+ packPath: packDir,
128
+ archiveUrl: manifest.distribution?.url,
129
+ checksum: manifest.distribution?.checksum,
130
+ preferredMode: 'lexical',
131
+ };
132
+ summary.Source = 'E Knowledge Hub';
133
+ summary.Provider = manifest.displayName || manifest.name;
134
+ summary.Package = target;
135
+ summary.Version = manifest.version;
136
+ summary['E-Pack'] = `${manifest.displayName || manifest.name} (Local @ ${packDir})`;
175
137
  }
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
138
  return {
183
139
  config,
184
140
  summary,
@@ -110,16 +110,18 @@ describe('Guided Manifest-Driven Configuration UX Specification Tests', () => {
110
110
  });
111
111
  });
112
112
  describe('Discrepancy #2 & #3: Knowledge Choices & Manifest Metadata Confirmation', () => {
113
- test('Selecting "Do not use knowledge" sets provider: none and skips discovery', async () => {
114
- inquirer_1.default.prompt.mockResolvedValueOnce({ source: 'none' });
113
+ test('Declining external pack uses sovereign Life Database by default and skips discovery', async () => {
114
+ inquirer_1.default.prompt.mockResolvedValueOnce({ useExternalPack: false });
115
115
  const result = await (0, knowledge_1.configureKnowledge)({
116
116
  companionName: 'Sparkle',
117
117
  manifest: knowledgeManifest,
118
118
  });
119
- expect(result.config.provider).toBe('none');
120
- expect(result.summary?.Source).toBe('Do not use knowledge');
119
+ expect(result.config.provider).toBe('unified');
120
+ expect(result.config.lifeDatabase).toBe(true);
121
+ expect(result.summary?.['Life Database']).toBe('Enabled (siduri.sqlite)');
122
+ expect(result.summary?.['E-Pack']).toBe('None');
121
123
  });
122
- test('Selecting "E Knowledge Hub" discovers manifest, extracts capabilities, and confirms provider', async () => {
124
+ test('Attaching E Knowledge Hub pack discovers manifest and configures remote provider automatically', async () => {
123
125
  const mockClient = {
124
126
  resolveProvider: jest.fn().mockResolvedValue({
125
127
  name: 'e-teyvat',
@@ -128,13 +130,17 @@ describe('Guided Manifest-Driven Configuration UX Specification Tests', () => {
128
130
  version: '1.2.0',
129
131
  description: 'Teyvat knowledge provider for Siduri.',
130
132
  capabilities: ['Search', 'Retrieval', 'Context injection'],
133
+ distributionType: 'remote',
134
+ distribution: {
135
+ kind: 'provider',
136
+ url: 'https://teyvat.e.vxnus.xyz',
137
+ },
131
138
  source: 'E Knowledge Hub',
132
139
  }),
133
140
  };
134
141
  inquirer_1.default.prompt
135
- .mockResolvedValueOnce({ source: 'e-hub' }) // Knowledge source
136
- .mockResolvedValueOnce({ packId: '@vxnus/e-teyvat' }) // Package ID
137
- .mockResolvedValueOnce({ confirmProvider: 'yes' }); // Confirm provider
142
+ .mockResolvedValueOnce({ useExternalPack: true }) // Attach external pack
143
+ .mockResolvedValueOnce({ packId: '@vxnus/e-teyvat' }); // Package ID
138
144
  const result = await (0, knowledge_1.configureKnowledge)({ companionName: 'Sparkle', manifest: knowledgeManifest }, { client: mockClient });
139
145
  expect(result.config.provider).toBe('e-hub');
140
146
  expect(result.config.packId).toBe('@vxnus/e-teyvat');
@@ -142,6 +148,33 @@ describe('Guided Manifest-Driven Configuration UX Specification Tests', () => {
142
148
  expect(result.summary?.Provider).toBe('E Teyvat');
143
149
  expect(result.summary?.Package).toBe('@vxnus/e-teyvat');
144
150
  expect(result.summary?.Version).toBe('1.2.0');
151
+ expect(result.summary?.['E-Pack']).toContain('Remote');
152
+ });
153
+ test('Attaching hybrid pack allows choosing between remote and local with automated path', async () => {
154
+ const mockClient = {
155
+ resolveProvider: jest.fn().mockResolvedValue({
156
+ name: 'e-teyvat',
157
+ displayName: 'E Teyvat',
158
+ package: '@vxnus/e-teyvat',
159
+ version: '1.2.0',
160
+ description: 'Teyvat knowledge provider for Siduri.',
161
+ capabilities: ['Search', 'Retrieval', 'Context injection'],
162
+ distributionType: 'both',
163
+ distribution: {
164
+ kind: 'archive',
165
+ url: 'https://knowledge.e.vxnus.xyz/archives/teyvat.tar.gz',
166
+ },
167
+ source: 'E Knowledge Hub',
168
+ }),
169
+ };
170
+ inquirer_1.default.prompt
171
+ .mockResolvedValueOnce({ useExternalPack: true })
172
+ .mockResolvedValueOnce({ packId: '@vxnus/e-teyvat' })
173
+ .mockResolvedValueOnce({ selectedMode: 'local' });
174
+ const result = await (0, knowledge_1.configureKnowledge)({ companionName: 'Sparkle', manifest: knowledgeManifest }, { client: mockClient });
175
+ expect(result.config.provider).toBe('e-knowledge');
176
+ expect(result.config.packPath).toBe('./assets/knowledge/vxnus-e-teyvat');
177
+ expect(result.summary?.['E-Pack']).toContain('Local @ ./assets/knowledge/vxnus-e-teyvat');
145
178
  });
146
179
  test('Knowledge manifest validation enforces name and version', () => {
147
180
  expect(() => (0, knowledge_hub_1.validateKnowledgeManifest)({})).toThrow('Invalid knowledge manifest');
package/dist/generator.js CHANGED
@@ -54,7 +54,9 @@ function getDefaultConfigForManifest(manifest) {
54
54
  config.providers = config.providers || [];
55
55
  }
56
56
  else if (manifest.organType === 'knowledge') {
57
- config.provider = config.provider || 'none';
57
+ config.provider = config.provider || 'unified';
58
+ config.lifeDatabase = config.lifeDatabase ?? true;
59
+ config.dbPath = config.dbPath || 'siduri.sqlite';
58
60
  }
59
61
  else if (manifest.organType === 'behavior') {
60
62
  config.provider = config.provider || 'active_self';
@@ -72,7 +74,7 @@ function getDefaultConfigForManifest(manifest) {
72
74
  function generateInstanceFiles(options) {
73
75
  const instanceName = options.name || 'my-siduri';
74
76
  const instanceId = options.id || 'default';
75
- const coreVersion = options.coreVersion || '^2.0.2';
77
+ const coreVersion = options.coreVersion || '^2.0.3';
76
78
  const manifests = options.selectedManifests;
77
79
  const hasMemory = manifests.some((m) => m.organType === 'memory');
78
80
  const hasVoice = manifests.some((m) => m.organType === 'voice');
@@ -266,7 +268,7 @@ function generateInstanceFiles(options) {
266
268
  ` // API: Version info`,
267
269
  ` if (pathname === '/version' && req.method === 'GET') {`,
268
270
  ` res.writeHead(200, { 'Content-Type': 'application/json' });`,
269
- ` res.end(JSON.stringify({ version: '2.0.2', name: config.name, id: config.id }));`,
271
+ ` res.end(JSON.stringify({ version: '2.0.3', name: config.name, id: config.id }));`,
270
272
  ` return;`,
271
273
  ` }`,
272
274
  '',
@@ -673,6 +675,12 @@ function generateInstanceFiles(options) {
673
675
  createAssetsDirs.push(`assets/voice/${companionSlug}`);
674
676
  readmeLines.push('', '### Voice & RVC Models', `Place your character RVC voice models into \`./assets/voice/${companionSlug}/\`:`, `- \`${companionSlug}.pth\` (Target voice weights)`, `- \`${companionSlug}.index\` (Feature index file)`);
675
677
  }
678
+ const knowledgeConfig = options.organConfigs?.knowledge || options.organConfigs?.['@siduri-x/knowledge'];
679
+ if (manifests.some((m) => m.organType === 'knowledge') && knowledgeConfig?.packPath) {
680
+ const cleanPackDir = String(knowledgeConfig.packPath).replace(/^\.\//, '');
681
+ createAssetsDirs.push(cleanPackDir);
682
+ readmeLines.push('', '### Knowledge Pack Assets', `Local knowledge pack files reside in \`./${cleanPackDir}/\`.`);
683
+ }
676
684
  readmeLines.push('');
677
685
  const readmeMd = readmeLines.join('\n');
678
686
  const webHtml = (0, web_template_1.generateWebHtml)(instanceName, manifests);
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { OrganManifest } from './manifest';
3
- export declare const CLI_VERSION = "2.0.2";
3
+ export declare const CLI_VERSION = "2.0.4";
4
4
  export declare const colors: {
5
5
  cyan: string;
6
6
  dim: string;
package/dist/index.js CHANGED
@@ -26,7 +26,7 @@ const doctor_1 = require("./doctor");
26
26
  const db_1 = require("./db");
27
27
  const configurators_1 = require("./configurators");
28
28
  const execFile = (0, node_util_1.promisify)(node_child_process_1.execFile);
29
- exports.CLI_VERSION = '2.0.2';
29
+ exports.CLI_VERSION = '2.0.4';
30
30
  exports.colors = {
31
31
  cyan: '\u001b[36m',
32
32
  dim: '\u001b[2m',
@@ -120,22 +120,10 @@ async function runCreateWizard(targetDir) {
120
120
  ]);
121
121
  const companionName = basicAnswers.name;
122
122
  const projectDir = targetDir ? node_path_1.default.resolve(process.cwd(), targetDir) : node_path_1.default.resolve(process.cwd(), projectDirectoryName(companionName));
123
- printSection('Organ Configuration');
124
- console.log(`${exports.colors.dim}Configure each capability organ for your standalone companion instance.${exports.colors.reset}\n`);
125
- const selectedManifests = [];
126
- const organConfigs = {};
127
- const organSummaries = {};
128
- // Brain is required by architecture contract for cognition
129
- const brainManifest = registry.get('brain') || availableManifests.find((m) => m.organType === 'brain');
130
- if (brainManifest) {
131
- selectedManifests.push(brainManifest);
132
- printSection('Brain · required');
133
- const res = await (0, configurators_1.configureOrgan)(brainManifest, {
134
- companionName,
135
- });
136
- organConfigs[brainManifest.configKey] = res.config;
137
- organSummaries[brainManifest.configKey] = res.summary || {};
138
- }
123
+ printSection('Organ Selection');
124
+ console.log(`${exports.colors.dim}In Siduri, a companion is assembled from modular capability organs:${exports.colors.reset}`);
125
+ console.log(`${exports.colors.dim}• Brain provides central cognition and LLM reasoning (required).${exports.colors.reset}`);
126
+ console.log(`${exports.colors.dim}• Select any capability organs below to attach to ${companionName}.${exports.colors.reset}\n`);
139
127
  // Canonical organ presentation order
140
128
  const canonicalOrder = ['memory', 'knowledge', 'behavior', 'voice', 'body', 'mouth', 'hands', 'vision', 'ear', 'observation'];
141
129
  const nonBrainManifests = availableManifests
@@ -147,19 +135,37 @@ async function runCreateWizard(targetDir) {
147
135
  const valB = idxB === -1 ? 99 : idxB;
148
136
  return valA - valB;
149
137
  });
150
- for (const m of nonBrainManifests) {
151
- const isRecommended = ['memory', 'knowledge', 'behavior', 'voice', 'body', 'mouth'].includes(m.organType);
152
- const { enableOrgan } = await inquirer_1.default.prompt({
153
- type: 'confirm',
154
- name: 'enableOrgan',
155
- message: `Enable ${m.displayName}?`,
156
- default: isRecommended,
157
- });
158
- if (!enableOrgan) {
159
- continue;
160
- }
161
- selectedManifests.push(m);
162
- const sectionTitle = m.displayName.split(' ')[0] || m.organType;
138
+ const isRecommendedType = (type) => ['memory', 'knowledge', 'behavior', 'voice', 'body', 'mouth'].includes(type);
139
+ const organChoices = nonBrainManifests.map((m) => ({
140
+ name: `${m.displayName} ${exports.colors.dim} ${m.description}${exports.colors.reset}`,
141
+ value: m.organType,
142
+ checked: isRecommendedType(m.organType),
143
+ }));
144
+ const { selectedOrganTypes } = await inquirer_1.default.prompt({
145
+ type: 'checkbox',
146
+ name: 'selectedOrganTypes',
147
+ message: 'Select capability organs to enable:',
148
+ choices: organChoices,
149
+ });
150
+ const selectedManifests = [];
151
+ const organConfigs = {};
152
+ const organSummaries = {};
153
+ // Brain is required by architecture contract for cognition
154
+ const brainManifest = registry.get('brain') || availableManifests.find((m) => m.organType === 'brain');
155
+ if (brainManifest) {
156
+ selectedManifests.push(brainManifest);
157
+ }
158
+ for (const organType of selectedOrganTypes) {
159
+ const m = registry.get(organType) || availableManifests.find((item) => item.organType === organType);
160
+ if (m)
161
+ selectedManifests.push(m);
162
+ }
163
+ // 2. Interactive Organ Configuration Stage
164
+ printSection('Organ Configuration');
165
+ console.log(`${exports.colors.dim}Configure each enabled organ for ${companionName}.${exports.colors.reset}\n`);
166
+ for (const m of selectedManifests) {
167
+ const isRequired = m.organType === 'brain';
168
+ const sectionTitle = isRequired ? `${m.displayName.split(' ')[0] || m.organType} · required` : m.displayName.split(' ')[0] || m.organType;
163
169
  printSection(sectionTitle);
164
170
  const res = await (0, configurators_1.configureOrgan)(m, {
165
171
  companionName,
@@ -258,6 +264,27 @@ async function runCreateWizard(targetDir) {
258
264
  else if (files.createAssetsBodyDir) {
259
265
  await (0, promises_1.mkdir)(node_path_1.default.join(projectDir, 'assets/body'), { recursive: true });
260
266
  }
267
+ // If local knowledge archive is configured with a download URL, automatically fetch and unpack it
268
+ const knowledgeConfig = organConfigs.knowledge || organConfigs['@siduri-x/knowledge'];
269
+ if (knowledgeConfig?.pack?.mode === 'local' && knowledgeConfig?.pack?.archiveUrl) {
270
+ const packRelDir = (knowledgeConfig.packPath || 'assets/knowledge/pack').replace(/^\.\//, '');
271
+ const packDest = node_path_1.default.join(projectDir, packRelDir);
272
+ try {
273
+ await withTask(`Downloading knowledge archive (${knowledgeConfig.pack.packId || 'pack'})`, async () => {
274
+ const res = await fetch(knowledgeConfig.pack.archiveUrl);
275
+ if (res.ok) {
276
+ const tarPath = node_path_1.default.join(packDest, 'pack.tar.gz');
277
+ const buffer = Buffer.from(await res.arrayBuffer());
278
+ await (0, promises_1.writeFile)(tarPath, buffer);
279
+ await execFile('tar', ['-xzf', tarPath, '-C', packDest]);
280
+ await node_fs_1.default.promises.unlink(tarPath).catch(() => { });
281
+ }
282
+ });
283
+ }
284
+ catch (err) {
285
+ console.warn(`${exports.colors.yellow}!${exports.colors.reset} Notice: Could not download or unpack knowledge archive: ${err.message}`);
286
+ }
287
+ }
261
288
  printSuccess(`Generated standalone files at ${projectDir}`);
262
289
  // 5. Install dependencies from registry
263
290
  try {
@@ -14,10 +14,17 @@ export interface KnowledgeHubManifest {
14
14
  version: string;
15
15
  description?: string;
16
16
  capabilities?: KnowledgeHubCapabilities | string[];
17
+ distributionType?: 'local' | 'remote' | 'both';
17
18
  distribution?: {
18
- kind?: string;
19
+ kind?: 'archive' | 'provider' | string;
19
20
  url?: string;
21
+ checksum?: string;
20
22
  };
23
+ distributions?: Array<{
24
+ kind?: 'archive' | 'provider' | string;
25
+ url?: string;
26
+ checksum?: string;
27
+ }>;
21
28
  source?: string;
22
29
  environment?: Array<{
23
30
  name: string;
@@ -13,6 +13,7 @@ exports.KNOWN_KNOWLEDGE_PACKS = {
13
13
  version: '1.2.0',
14
14
  description: 'Teyvat knowledge provider for Siduri.',
15
15
  capabilities: ['Search', 'Retrieval', 'Context injection'],
16
+ distributionType: 'remote',
16
17
  distribution: {
17
18
  kind: 'provider',
18
19
  url: 'https://teyvat.e.vxnus.xyz',
@@ -31,6 +32,18 @@ function validateKnowledgeManifest(raw, packId) {
31
32
  if (!m.version || typeof m.version !== 'string') {
32
33
  throw new Error(`Invalid knowledge manifest: missing 'version' field.`);
33
34
  }
35
+ const distributions = m.distributions || (m.distribution ? [m.distribution] : []);
36
+ let distributionType = m.distributionType;
37
+ if (!distributionType) {
38
+ const hasArchive = distributions.some((d) => d.kind === 'archive');
39
+ const hasProvider = distributions.some((d) => d.kind === 'provider');
40
+ if (hasArchive && hasProvider)
41
+ distributionType = 'both';
42
+ else if (hasProvider)
43
+ distributionType = 'remote';
44
+ else
45
+ distributionType = 'local';
46
+ }
34
47
  return {
35
48
  name: m.name,
36
49
  displayName: m.displayName || m.name,
@@ -39,7 +52,9 @@ function validateKnowledgeManifest(raw, packId) {
39
52
  version: m.version,
40
53
  description: m.description || 'Knowledge pack provider',
41
54
  capabilities: m.capabilities || ['Search', 'Retrieval'],
55
+ distributionType,
42
56
  distribution: m.distribution,
57
+ distributions: m.distributions,
43
58
  source: m.source || 'E Knowledge Hub',
44
59
  environment: m.environment || [],
45
60
  };
@@ -127,6 +142,12 @@ function displayKnowledgeProviderSummary(manifest, packId) {
127
142
  console.log(` ${dim}Name:${reset} ${manifest.displayName || manifest.name}`);
128
143
  console.log(` ${dim}Package:${reset} ${packId}`);
129
144
  console.log(` ${dim}Version:${reset} ${manifest.version}`);
145
+ const typeLabel = manifest.distributionType === 'both'
146
+ ? 'Hybrid (Available as Remote Provider & Local Archive)'
147
+ : manifest.distributionType === 'remote'
148
+ ? 'Remote Provider'
149
+ : 'Local Archive';
150
+ console.log(` ${dim}Distribution:${reset} ${typeLabel}`);
130
151
  if (manifest.description) {
131
152
  console.log(`\n ${dim}Description:${reset}`);
132
153
  console.log(` ${manifest.description}`);