siduri 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +6 -0
- package/README.md +84 -0
- package/dist/builtin-manifests.d.ts +2 -0
- package/dist/builtin-manifests.js +524 -0
- package/dist/clean-machine-e2e.test.d.ts +1 -0
- package/dist/clean-machine-e2e.test.js +160 -0
- package/dist/clean-machine-smoke.test.d.ts +1 -0
- package/dist/clean-machine-smoke.test.js +165 -0
- package/dist/colors.d.ts +8 -0
- package/dist/colors.js +11 -0
- package/dist/configurators/behavior.d.ts +2 -0
- package/dist/configurators/behavior.js +77 -0
- package/dist/configurators/body.d.ts +2 -0
- package/dist/configurators/body.js +107 -0
- package/dist/configurators/brain.d.ts +6 -0
- package/dist/configurators/brain.js +80 -0
- package/dist/configurators/ear.d.ts +2 -0
- package/dist/configurators/ear.js +43 -0
- package/dist/configurators/hands.d.ts +2 -0
- package/dist/configurators/hands.js +43 -0
- package/dist/configurators/index.d.ts +24 -0
- package/dist/configurators/index.js +78 -0
- package/dist/configurators/knowledge.d.ts +6 -0
- package/dist/configurators/knowledge.js +146 -0
- package/dist/configurators/memory.d.ts +2 -0
- package/dist/configurators/memory.js +26 -0
- package/dist/configurators/mouth.d.ts +2 -0
- package/dist/configurators/mouth.js +35 -0
- package/dist/configurators/observation.d.ts +2 -0
- package/dist/configurators/observation.js +38 -0
- package/dist/configurators/types.d.ts +12 -0
- package/dist/configurators/types.js +2 -0
- package/dist/configurators/vision.d.ts +2 -0
- package/dist/configurators/vision.js +47 -0
- package/dist/configurators/voice.d.ts +2 -0
- package/dist/configurators/voice.js +373 -0
- package/dist/configurators.test.d.ts +1 -0
- package/dist/configurators.test.js +464 -0
- package/dist/db.d.ts +13 -0
- package/dist/db.js +15 -0
- package/dist/discovery.d.ts +13 -0
- package/dist/discovery.js +85 -0
- package/dist/discovery.test.d.ts +1 -0
- package/dist/discovery.test.js +69 -0
- package/dist/doctor-db.test.d.ts +1 -0
- package/dist/doctor-db.test.js +52 -0
- package/dist/doctor.d.ts +19 -0
- package/dist/doctor.js +266 -0
- package/dist/generator.d.ts +23 -0
- package/dist/generator.js +1094 -0
- package/dist/generator.test.d.ts +1 -0
- package/dist/generator.test.js +252 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +607 -0
- package/dist/manifest.d.ts +33 -0
- package/dist/manifest.js +40 -0
- package/dist/project-dir.test.d.ts +1 -0
- package/dist/project-dir.test.js +37 -0
- package/dist/providers/knowledge-hub.d.ts +44 -0
- package/dist/providers/knowledge-hub.js +162 -0
- package/dist/providers/openrouter.d.ts +32 -0
- package/dist/providers/openrouter.js +201 -0
- package/dist/release-check.d.ts +9 -0
- package/dist/release-check.js +128 -0
- package/dist/runtime-parity.test.d.ts +1 -0
- package/dist/runtime-parity.test.js +141 -0
- package/dist/schema-validator.d.ts +9 -0
- package/dist/schema-validator.js +107 -0
- package/dist/web-dist/404/index.html +1 -0
- package/dist/web-dist/404.html +1 -0
- package/dist/web-dist/__next.__PAGE__.txt +34 -0
- package/dist/web-dist/__next._full.txt +32 -0
- package/dist/web-dist/__next._tree.txt +4 -0
- package/dist/web-dist/_next/static/4aL_iP3FHsLSyHzCnRQYD/_buildManifest.js +1 -0
- package/dist/web-dist/_next/static/4aL_iP3FHsLSyHzCnRQYD/_ssgManifest.js +1 -0
- package/dist/web-dist/_next/static/chunks/136-1d10af93b770a1c8.js +52 -0
- package/dist/web-dist/_next/static/chunks/1629039a.f535b3ff45566416.js +1 -0
- package/dist/web-dist/_next/static/chunks/202.59b8eb8a8c45f711.js +1 -0
- package/dist/web-dist/_next/static/chunks/2be4ccb0.ab2975eb50b4e166.js +1 -0
- package/dist/web-dist/_next/static/chunks/411.d68b2e48424f1e47.js +1 -0
- package/dist/web-dist/_next/static/chunks/426-9f7f6e7fffd4c3c3.js +1 -0
- package/dist/web-dist/_next/static/chunks/563b947f-8e2871a2ab7cd91e.js +1 -0
- package/dist/web-dist/_next/static/chunks/92e71660.11f182d50bce2be0.js +342 -0
- package/dist/web-dist/_next/static/chunks/app/_global-error/page-bce6d30fb2b1a3b0.js +1 -0
- package/dist/web-dist/_next/static/chunks/app/_not-found/page-74658f4988eabcc7.js +1 -0
- package/dist/web-dist/_next/static/chunks/app/chat/page-ab56048b707a3ddd.js +34 -0
- package/dist/web-dist/_next/static/chunks/app/layout-14f1a3f31e58be17.js +1 -0
- package/dist/web-dist/_next/static/chunks/app/operator/page-6b2d0ba884183ad7.js +1 -0
- package/dist/web-dist/_next/static/chunks/app/page-fc4bf56cf33ae27b.js +1 -0
- package/dist/web-dist/_next/static/chunks/framework-7afb7035d9872ea0.js +1 -0
- package/dist/web-dist/_next/static/chunks/main-8553b996986f1210.js +5 -0
- package/dist/web-dist/_next/static/chunks/main-app-9e6a144626e5a5b9.js +1 -0
- package/dist/web-dist/_next/static/chunks/next/dist/client/components/builtin/app-error-bce6d30fb2b1a3b0.js +1 -0
- package/dist/web-dist/_next/static/chunks/next/dist/client/components/builtin/forbidden-bce6d30fb2b1a3b0.js +1 -0
- package/dist/web-dist/_next/static/chunks/next/dist/client/components/builtin/global-error-68ea4ac05dcaf701.js +1 -0
- package/dist/web-dist/_next/static/chunks/next/dist/client/components/builtin/not-found-bce6d30fb2b1a3b0.js +1 -0
- package/dist/web-dist/_next/static/chunks/next/dist/client/components/builtin/unauthorized-bce6d30fb2b1a3b0.js +1 -0
- package/dist/web-dist/_next/static/chunks/polyfills-42372ed130431b0a.js +1 -0
- package/dist/web-dist/_next/static/chunks/webpack-9073f434bc17ce16.js +1 -0
- package/dist/web-dist/_next/static/css/ceeb8e5596664b11.css +1 -0
- package/dist/web-dist/_not-found/__next._full.txt +17 -0
- package/dist/web-dist/_not-found/__next._not-found.__PAGE__.txt +23 -0
- package/dist/web-dist/_not-found/__next._tree.txt +3 -0
- package/dist/web-dist/_not-found/index.html +1 -0
- package/dist/web-dist/_not-found/index.txt +17 -0
- package/dist/web-dist/apple-icon.png +0 -0
- package/dist/web-dist/apple-touch-icon.png +0 -0
- package/dist/web-dist/chat/__next._full.txt +18 -0
- package/dist/web-dist/chat/__next._tree.txt +3 -0
- package/dist/web-dist/chat/__next.chat.__PAGE__.txt +24 -0
- package/dist/web-dist/chat/index.html +1 -0
- package/dist/web-dist/chat/index.txt +18 -0
- package/dist/web-dist/favicon.ico +0 -0
- package/dist/web-dist/favicon.svg +4 -0
- package/dist/web-dist/icon.svg +4 -0
- package/dist/web-dist/index.html +1 -0
- package/dist/web-dist/index.txt +32 -0
- package/dist/web-dist/live2d/live2dcubismcore.min.js +9 -0
- package/dist/web-dist/logo-circle.png +0 -0
- package/dist/web-dist/logo-circle.svg +4 -0
- package/dist/web-dist/logo-white.svg +3 -0
- package/dist/web-dist/logo.png +0 -0
- package/dist/web-dist/logo.svg +3 -0
- package/dist/web-dist/operator/__next._full.txt +18 -0
- package/dist/web-dist/operator/__next._tree.txt +3 -0
- package/dist/web-dist/operator/__next.operator.__PAGE__.txt +24 -0
- package/dist/web-dist/operator/index.html +1 -0
- package/dist/web-dist/operator/index.txt +18 -0
- package/dist/web-template.d.ts +2 -0
- package/dist/web-template.js +589 -0
- package/package.json +44 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const node_child_process_1 = require("node:child_process");
|
|
9
|
+
const discovery_1 = require("./discovery");
|
|
10
|
+
const generator_1 = require("./generator");
|
|
11
|
+
describe('Phase 5: Clean-Machine Distribution & E2E Integration Suite', () => {
|
|
12
|
+
const repoRoot = node_path_1.default.resolve(__dirname, '../../');
|
|
13
|
+
const tempPackDir = node_path_1.default.resolve(__dirname, '../temp-packs-e2e');
|
|
14
|
+
const cleanMachineRoot = node_path_1.default.resolve(__dirname, '../temp-clean-machine-e2e');
|
|
15
|
+
const getPkgVer = (dir) => JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.resolve(repoRoot, dir, 'package.json'), 'utf8')).version;
|
|
16
|
+
const ALL_CANONICAL_PACKAGES = [
|
|
17
|
+
{ filter: '@siduri-x/core', dir: 'packages/core', tarName: `siduri-x-core-${getPkgVer('packages/core')}.tgz`, isOrgan: false },
|
|
18
|
+
{ filter: '@siduri-x/brain', dir: 'packages/organs/brain', tarName: `siduri-x-brain-${getPkgVer('packages/organs/brain')}.tgz`, isOrgan: true },
|
|
19
|
+
{ filter: '@siduri-x/memory', dir: 'packages/memory', tarName: `siduri-x-memory-${getPkgVer('packages/memory')}.tgz`, isOrgan: true },
|
|
20
|
+
{ filter: '@siduri-x/knowledge', dir: 'packages/knowledge', tarName: `siduri-x-knowledge-${getPkgVer('packages/knowledge')}.tgz`, isOrgan: true },
|
|
21
|
+
{ filter: '@siduri-x/self', dir: 'packages/self', tarName: `siduri-x-self-${getPkgVer('packages/self')}.tgz`, isOrgan: true },
|
|
22
|
+
{ filter: '@siduri-x/ear', dir: 'packages/organs/ear', tarName: `siduri-x-ear-${getPkgVer('packages/organs/ear')}.tgz`, isOrgan: true },
|
|
23
|
+
{ filter: '@siduri-x/vision', dir: 'packages/organs/vision', tarName: `siduri-x-vision-${getPkgVer('packages/organs/vision')}.tgz`, isOrgan: true },
|
|
24
|
+
{ filter: '@siduri-x/hands', dir: 'packages/organs/hands', tarName: `siduri-x-hands-${getPkgVer('packages/organs/hands')}.tgz`, isOrgan: true },
|
|
25
|
+
{ filter: '@siduri-x/body', dir: 'packages/organs/body', tarName: `siduri-x-body-${getPkgVer('packages/organs/body')}.tgz`, isOrgan: true },
|
|
26
|
+
{ filter: '@siduri-x/voice', dir: 'packages/organs/voice', tarName: `siduri-x-voice-${getPkgVer('packages/organs/voice')}.tgz`, isOrgan: true },
|
|
27
|
+
{ filter: '@siduri-x/observation', dir: 'packages/organs/observation', tarName: `siduri-x-observation-${getPkgVer('packages/organs/observation')}.tgz`, isOrgan: true },
|
|
28
|
+
{ filter: '@siduri-x/mouth', dir: 'packages/organs/mouth', tarName: `siduri-x-mouth-${getPkgVer('packages/organs/mouth')}.tgz`, isOrgan: true },
|
|
29
|
+
{ filter: '@siduri-x/eknowledge', dir: 'packages/organs/eknowledge', tarName: `siduri-x-eknowledge-${getPkgVer('packages/organs/eknowledge')}.tgz`, isOrgan: true },
|
|
30
|
+
{ filter: '@vxnus/siduri', dir: 'cli', tarName: `vxnus-siduri-${getPkgVer('cli')}.tgz`, isOrgan: false },
|
|
31
|
+
];
|
|
32
|
+
const getTarPath = (pkgName) => {
|
|
33
|
+
const pkg = ALL_CANONICAL_PACKAGES.find(p => p.filter === pkgName);
|
|
34
|
+
return node_path_1.default.join(tempPackDir, pkg.tarName);
|
|
35
|
+
};
|
|
36
|
+
beforeAll(() => {
|
|
37
|
+
// 1. Prepare clean directories
|
|
38
|
+
if (node_fs_1.default.existsSync(tempPackDir))
|
|
39
|
+
node_fs_1.default.rmSync(tempPackDir, { recursive: true, force: true });
|
|
40
|
+
if (node_fs_1.default.existsSync(cleanMachineRoot))
|
|
41
|
+
node_fs_1.default.rmSync(cleanMachineRoot, { recursive: true, force: true });
|
|
42
|
+
node_fs_1.default.mkdirSync(tempPackDir, { recursive: true });
|
|
43
|
+
node_fs_1.default.mkdirSync(cleanMachineRoot, { recursive: true });
|
|
44
|
+
// 2. Build all packages in repo
|
|
45
|
+
(0, node_child_process_1.execSync)("pnpm turbo run build --filter='!web'", { cwd: repoRoot, stdio: 'pipe' });
|
|
46
|
+
// 3. Pack each canonical package into tempPackDir
|
|
47
|
+
for (const pkg of ALL_CANONICAL_PACKAGES) {
|
|
48
|
+
(0, node_child_process_1.execSync)(`pnpm pack --pack-destination ${tempPackDir}`, {
|
|
49
|
+
cwd: node_path_1.default.resolve(repoRoot, pkg.dir),
|
|
50
|
+
stdio: 'pipe',
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}, 90000);
|
|
54
|
+
afterAll(() => {
|
|
55
|
+
if (node_fs_1.default.existsSync(tempPackDir))
|
|
56
|
+
node_fs_1.default.rmSync(tempPackDir, { recursive: true, force: true });
|
|
57
|
+
if (node_fs_1.default.existsSync(cleanMachineRoot))
|
|
58
|
+
node_fs_1.default.rmSync(cleanMachineRoot, { recursive: true, force: true });
|
|
59
|
+
});
|
|
60
|
+
describe('Phase 5A: Package Artifact Verification', () => {
|
|
61
|
+
test('all 14 packages produce valid tarballs', () => {
|
|
62
|
+
for (const pkg of ALL_CANONICAL_PACKAGES) {
|
|
63
|
+
const tarPath = node_path_1.default.join(tempPackDir, pkg.tarName);
|
|
64
|
+
expect(node_fs_1.default.existsSync(tarPath)).toBe(true);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
test('packed package.json files have zero workspace:* or link: dependencies', () => {
|
|
68
|
+
for (const pkg of ALL_CANONICAL_PACKAGES) {
|
|
69
|
+
const tarPath = node_path_1.default.join(tempPackDir, pkg.tarName);
|
|
70
|
+
const pkgJsonRaw = (0, node_child_process_1.execSync)(`tar -xzf ${tarPath} -O package/package.json`, { encoding: 'utf8' });
|
|
71
|
+
const pkgJson = JSON.parse(pkgJsonRaw);
|
|
72
|
+
const allDeps = {
|
|
73
|
+
...(pkgJson.dependencies || {}),
|
|
74
|
+
...(pkgJson.peerDependencies || {}),
|
|
75
|
+
};
|
|
76
|
+
for (const [depName, version] of Object.entries(allDeps)) {
|
|
77
|
+
if (typeof version === 'string') {
|
|
78
|
+
expect(version.startsWith('workspace:')).toBe(false);
|
|
79
|
+
expect(version.startsWith('link:')).toBe(false);
|
|
80
|
+
expect(version.includes('../')).toBe(false);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
describe('Phase 5B & 5C & 5G: Clean-Machine Installation & Isolation Acceptance', () => {
|
|
87
|
+
const registry = discovery_1.OrganRegistry.discover([
|
|
88
|
+
node_path_1.default.resolve(repoRoot, 'packages/organs'),
|
|
89
|
+
node_path_1.default.resolve(repoRoot, 'packages')
|
|
90
|
+
]);
|
|
91
|
+
const brain = registry.get('brain');
|
|
92
|
+
const hands = registry.get('hands');
|
|
93
|
+
const memory = registry.get('memory');
|
|
94
|
+
test('Clean Composition 1: Brain only instance installs, starts, runs doctor and db push', () => {
|
|
95
|
+
const instanceDir = node_path_1.default.join(cleanMachineRoot, 'inst-brain-only');
|
|
96
|
+
node_fs_1.default.mkdirSync(node_path_1.default.join(instanceDir, 'src'), { recursive: true });
|
|
97
|
+
const files = (0, generator_1.generateInstanceFiles)({
|
|
98
|
+
name: 'CleanBrainOnly',
|
|
99
|
+
selectedManifests: [brain],
|
|
100
|
+
});
|
|
101
|
+
const pkgObj = JSON.parse(files['package.json']);
|
|
102
|
+
pkgObj.dependencies = {
|
|
103
|
+
'@siduri-x/core': `file:${getTarPath('@siduri-x/core')}`,
|
|
104
|
+
'@siduri-x/brain': `file:${getTarPath('@siduri-x/brain')}`,
|
|
105
|
+
};
|
|
106
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'package.json'), JSON.stringify(pkgObj, null, 2) + '\n');
|
|
107
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'siduri.config.json'), files['siduri.config.json']);
|
|
108
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'siduri.schema.json'), files['siduri.schema.json']);
|
|
109
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, '.env.example'), files['.env.example']);
|
|
110
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'README.md'), files['README.md']);
|
|
111
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'src/index.js'), files['src/index.js']);
|
|
112
|
+
(0, node_child_process_1.execSync)('npm install --no-audit --no-fund', { cwd: instanceDir, stdio: 'pipe' });
|
|
113
|
+
expect(node_fs_1.default.existsSync(node_path_1.default.join(instanceDir, 'node_modules/@siduri-x/memory'))).toBe(false);
|
|
114
|
+
expect(node_fs_1.default.existsSync(node_path_1.default.join(instanceDir, 'node_modules/@siduri-x/hands'))).toBe(false);
|
|
115
|
+
const doctorPass = (0, node_child_process_1.execSync)(`node ${node_path_1.default.resolve(repoRoot, 'cli/dist/index.js')} doctor`, {
|
|
116
|
+
cwd: instanceDir,
|
|
117
|
+
env: { ...process.env, OPENROUTER_API_KEY: 'test-key-clean' },
|
|
118
|
+
encoding: 'utf8',
|
|
119
|
+
});
|
|
120
|
+
expect(doctorPass).toContain('Result: PASS');
|
|
121
|
+
const dbPushOutput = (0, node_child_process_1.execSync)(`node ${node_path_1.default.resolve(repoRoot, 'cli/dist/index.js')} db push`, {
|
|
122
|
+
cwd: instanceDir,
|
|
123
|
+
encoding: 'utf8',
|
|
124
|
+
});
|
|
125
|
+
expect(dbPushOutput).toContain('SQLite manages schema natively. No CLI migrations required.');
|
|
126
|
+
}, 60000);
|
|
127
|
+
test('Clean Composition 2: Brain + Hands installs and runs doctor verifying ACTION_POLICY_SECRET', () => {
|
|
128
|
+
const instanceDir = node_path_1.default.join(cleanMachineRoot, 'inst-brain-hands');
|
|
129
|
+
node_fs_1.default.mkdirSync(node_path_1.default.join(instanceDir, 'src'), { recursive: true });
|
|
130
|
+
const files = (0, generator_1.generateInstanceFiles)({
|
|
131
|
+
name: 'CleanBrainHands',
|
|
132
|
+
selectedManifests: [brain, hands],
|
|
133
|
+
});
|
|
134
|
+
const pkgObj = JSON.parse(files['package.json']);
|
|
135
|
+
pkgObj.dependencies = {
|
|
136
|
+
'@siduri-x/core': `file:${getTarPath('@siduri-x/core')}`,
|
|
137
|
+
'@siduri-x/brain': `file:${getTarPath('@siduri-x/brain')}`,
|
|
138
|
+
'@siduri-x/hands': `file:${getTarPath('@siduri-x/hands')}`,
|
|
139
|
+
};
|
|
140
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'package.json'), JSON.stringify(pkgObj, null, 2) + '\n');
|
|
141
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'siduri.config.json'), files['siduri.config.json']);
|
|
142
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'siduri.schema.json'), files['siduri.schema.json']);
|
|
143
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, '.env.example'), files['.env.example']);
|
|
144
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'README.md'), files['README.md']);
|
|
145
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'src/index.js'), files['src/index.js']);
|
|
146
|
+
(0, node_child_process_1.execSync)('npm install --no-audit --no-fund', { cwd: instanceDir, stdio: 'pipe' });
|
|
147
|
+
const doctorOutput = (0, node_child_process_1.execSync)(`node ${node_path_1.default.resolve(repoRoot, 'cli/dist/index.js')} doctor`, {
|
|
148
|
+
cwd: instanceDir,
|
|
149
|
+
env: {
|
|
150
|
+
...process.env,
|
|
151
|
+
OPENROUTER_API_KEY: 'test-key-clean',
|
|
152
|
+
ACTION_POLICY_SECRET: 'test-secret-clean',
|
|
153
|
+
},
|
|
154
|
+
encoding: 'utf8',
|
|
155
|
+
});
|
|
156
|
+
expect(doctorOutput).toContain('ACTION_POLICY_SECRET');
|
|
157
|
+
expect(doctorOutput).toContain('Result: PASS');
|
|
158
|
+
}, 60000);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const node_child_process_1 = require("node:child_process");
|
|
9
|
+
const discovery_1 = require("./discovery");
|
|
10
|
+
const generator_1 = require("./generator");
|
|
11
|
+
describe('Phase 5: Clean-Machine Distribution & Packaging Smoke Suite', () => {
|
|
12
|
+
const repoRoot = node_path_1.default.resolve(__dirname, '../../');
|
|
13
|
+
const tempPackDir = node_path_1.default.resolve(__dirname, '../temp-packs-smoke');
|
|
14
|
+
const cleanMachineRoot = node_path_1.default.resolve(__dirname, '../temp-clean-machine-smoke');
|
|
15
|
+
const getPkgVer = (dir) => JSON.parse(node_fs_1.default.readFileSync(node_path_1.default.resolve(repoRoot, dir, 'package.json'), 'utf8')).version;
|
|
16
|
+
const ALL_CANONICAL_PACKAGES = [
|
|
17
|
+
{ filter: '@sidurijs/core', dir: 'packages/core', tarName: `sidurijs-core-${getPkgVer('packages/core')}.tgz`, isOrgan: false },
|
|
18
|
+
{ filter: '@sidurijs/brain', dir: 'packages/organs/brain', tarName: `sidurijs-brain-${getPkgVer('packages/organs/brain')}.tgz`, isOrgan: true },
|
|
19
|
+
{ filter: '@sidurijs/archive', dir: 'packages/archive', tarName: `sidurijs-archive-${getPkgVer('packages/archive')}.tgz`, isOrgan: true },
|
|
20
|
+
{ filter: '@sidurijs/knowledge', dir: 'packages/knowledge', tarName: `sidurijs-knowledge-${getPkgVer('packages/knowledge')}.tgz`, isOrgan: true },
|
|
21
|
+
{ filter: '@sidurijs/self', dir: 'packages/self', tarName: `sidurijs-self-${getPkgVer('packages/self')}.tgz`, isOrgan: true },
|
|
22
|
+
{ filter: '@sidurijs/ear', dir: 'packages/organs/ear', tarName: `sidurijs-ear-${getPkgVer('packages/organs/ear')}.tgz`, isOrgan: true },
|
|
23
|
+
{ filter: '@sidurijs/vision', dir: 'packages/organs/vision', tarName: `sidurijs-vision-${getPkgVer('packages/organs/vision')}.tgz`, isOrgan: true },
|
|
24
|
+
{ filter: '@sidurijs/hands', dir: 'packages/organs/hands', tarName: `sidurijs-hands-${getPkgVer('packages/organs/hands')}.tgz`, isOrgan: true },
|
|
25
|
+
{ filter: '@sidurijs/body', dir: 'packages/organs/body', tarName: `sidurijs-body-${getPkgVer('packages/organs/body')}.tgz`, isOrgan: true },
|
|
26
|
+
{ filter: '@sidurijs/voice', dir: 'packages/organs/voice', tarName: `sidurijs-voice-${getPkgVer('packages/organs/voice')}.tgz`, isOrgan: true },
|
|
27
|
+
{ filter: '@sidurijs/observation', dir: 'packages/organs/observation', tarName: `sidurijs-observation-${getPkgVer('packages/organs/observation')}.tgz`, isOrgan: true },
|
|
28
|
+
{ filter: '@sidurijs/mouth', dir: 'packages/organs/mouth', tarName: `sidurijs-mouth-${getPkgVer('packages/organs/mouth')}.tgz`, isOrgan: true },
|
|
29
|
+
{ filter: '@vxnus/siduri', dir: 'cli', tarName: `vxnus-siduri-${getPkgVer('cli')}.tgz`, isOrgan: false },
|
|
30
|
+
];
|
|
31
|
+
const getTarPath = (pkgName) => {
|
|
32
|
+
const pkg = ALL_CANONICAL_PACKAGES.find(p => p.filter === pkgName);
|
|
33
|
+
return node_path_1.default.join(tempPackDir, pkg.tarName);
|
|
34
|
+
};
|
|
35
|
+
beforeAll(() => {
|
|
36
|
+
// 1. Prepare clean directories
|
|
37
|
+
if (node_fs_1.default.existsSync(tempPackDir))
|
|
38
|
+
node_fs_1.default.rmSync(tempPackDir, { recursive: true, force: true });
|
|
39
|
+
if (node_fs_1.default.existsSync(cleanMachineRoot))
|
|
40
|
+
node_fs_1.default.rmSync(cleanMachineRoot, { recursive: true, force: true });
|
|
41
|
+
node_fs_1.default.mkdirSync(tempPackDir, { recursive: true });
|
|
42
|
+
node_fs_1.default.mkdirSync(cleanMachineRoot, { recursive: true });
|
|
43
|
+
// 2. Build all packages in repo
|
|
44
|
+
(0, node_child_process_1.execSync)("pnpm turbo run build --filter='!web'", { cwd: repoRoot, stdio: 'pipe' });
|
|
45
|
+
// 3. Pack each canonical package into tempPackDir
|
|
46
|
+
for (const pkg of ALL_CANONICAL_PACKAGES) {
|
|
47
|
+
(0, node_child_process_1.execSync)(`pnpm pack --pack-destination ${tempPackDir}`, {
|
|
48
|
+
cwd: node_path_1.default.resolve(repoRoot, pkg.dir),
|
|
49
|
+
stdio: 'pipe',
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
}, 90000);
|
|
53
|
+
afterAll(() => {
|
|
54
|
+
if (node_fs_1.default.existsSync(tempPackDir))
|
|
55
|
+
node_fs_1.default.rmSync(tempPackDir, { recursive: true, force: true });
|
|
56
|
+
if (node_fs_1.default.existsSync(cleanMachineRoot))
|
|
57
|
+
node_fs_1.default.rmSync(cleanMachineRoot, { recursive: true, force: true });
|
|
58
|
+
});
|
|
59
|
+
describe('Phase 5A: Package Artifact Verification', () => {
|
|
60
|
+
test('all 14 packages produce valid tarballs', () => {
|
|
61
|
+
for (const pkg of ALL_CANONICAL_PACKAGES) {
|
|
62
|
+
const tarPath = node_path_1.default.join(tempPackDir, pkg.tarName);
|
|
63
|
+
expect(node_fs_1.default.existsSync(tarPath)).toBe(true);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
test('packed package.json files have zero workspace:* or link: dependencies', () => {
|
|
67
|
+
for (const pkg of ALL_CANONICAL_PACKAGES) {
|
|
68
|
+
const tarPath = node_path_1.default.join(tempPackDir, pkg.tarName);
|
|
69
|
+
const pkgJsonRaw = (0, node_child_process_1.execSync)(`tar -xzf ${tarPath} -O package/package.json`, { encoding: 'utf8' });
|
|
70
|
+
const pkgJson = JSON.parse(pkgJsonRaw);
|
|
71
|
+
const allDeps = {
|
|
72
|
+
...(pkgJson.dependencies || {}),
|
|
73
|
+
...(pkgJson.peerDependencies || {}),
|
|
74
|
+
};
|
|
75
|
+
for (const [depName, version] of Object.entries(allDeps)) {
|
|
76
|
+
if (typeof version === 'string') {
|
|
77
|
+
expect(version.startsWith('workspace:')).toBe(false);
|
|
78
|
+
expect(version.startsWith('link:')).toBe(false);
|
|
79
|
+
expect(version.includes('../')).toBe(false);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
describe('Phase 5B & 5C & 5G: Clean-Machine Installation & Isolation Acceptance', () => {
|
|
86
|
+
const registry = discovery_1.OrganRegistry.discover([
|
|
87
|
+
node_path_1.default.resolve(repoRoot, 'packages/organs'),
|
|
88
|
+
node_path_1.default.resolve(repoRoot, 'packages')
|
|
89
|
+
]);
|
|
90
|
+
const brain = registry.get('brain');
|
|
91
|
+
const hands = registry.get('hands');
|
|
92
|
+
const memory = registry.get('memory');
|
|
93
|
+
test('Clean Composition 1: Brain only instance installs, starts, runs doctor and db push', () => {
|
|
94
|
+
const instanceDir = node_path_1.default.join(cleanMachineRoot, 'inst-brain-only');
|
|
95
|
+
node_fs_1.default.mkdirSync(node_path_1.default.join(instanceDir, 'src'), { recursive: true });
|
|
96
|
+
const files = (0, generator_1.generateInstanceFiles)({
|
|
97
|
+
name: 'CleanBrainOnly',
|
|
98
|
+
selectedManifests: [brain],
|
|
99
|
+
});
|
|
100
|
+
const pkgObj = JSON.parse(files['package.json']);
|
|
101
|
+
pkgObj.dependencies = {
|
|
102
|
+
'@sidurijs/core': `file:${getTarPath('@sidurijs/core')}`,
|
|
103
|
+
'@sidurijs/brain': `file:${getTarPath('@sidurijs/brain')}`,
|
|
104
|
+
};
|
|
105
|
+
pkgObj.devDependencies = {
|
|
106
|
+
'@vxnus/siduri': `file:${getTarPath('@vxnus/siduri')}`,
|
|
107
|
+
};
|
|
108
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'package.json'), JSON.stringify(pkgObj, null, 2) + '\n');
|
|
109
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'siduri.config.json'), files['siduri.config.json']);
|
|
110
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'siduri.schema.json'), files['siduri.schema.json']);
|
|
111
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, '.env.example'), files['.env.example']);
|
|
112
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'README.md'), files['README.md']);
|
|
113
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'src/index.js'), files['src/index.js']);
|
|
114
|
+
(0, node_child_process_1.execSync)('npm install --no-audit --no-fund', { cwd: instanceDir, stdio: 'pipe' });
|
|
115
|
+
expect(node_fs_1.default.existsSync(node_path_1.default.join(instanceDir, 'node_modules/@sidurijs/archive'))).toBe(false);
|
|
116
|
+
expect(node_fs_1.default.existsSync(node_path_1.default.join(instanceDir, 'node_modules/@sidurijs/hands'))).toBe(false);
|
|
117
|
+
const doctorPass = (0, node_child_process_1.execSync)(`node ${node_path_1.default.resolve(repoRoot, 'cli/dist/index.js')} doctor`, {
|
|
118
|
+
cwd: instanceDir,
|
|
119
|
+
env: { ...process.env, OPENROUTER_API_KEY: 'test-key-clean' },
|
|
120
|
+
encoding: 'utf8',
|
|
121
|
+
});
|
|
122
|
+
expect(doctorPass).toContain('Result: PASS');
|
|
123
|
+
const dbPushOutput = (0, node_child_process_1.execSync)(`node ${node_path_1.default.resolve(repoRoot, 'cli/dist/index.js')} db push`, {
|
|
124
|
+
cwd: instanceDir,
|
|
125
|
+
encoding: 'utf8',
|
|
126
|
+
});
|
|
127
|
+
expect(dbPushOutput).toContain('SQLite manages schema natively. No CLI migrations required.');
|
|
128
|
+
}, 60000);
|
|
129
|
+
test('Clean Composition 2: Brain + Hands installs and runs doctor verifying ACTION_POLICY_SECRET', () => {
|
|
130
|
+
const instanceDir = node_path_1.default.join(cleanMachineRoot, 'inst-brain-hands');
|
|
131
|
+
node_fs_1.default.mkdirSync(node_path_1.default.join(instanceDir, 'src'), { recursive: true });
|
|
132
|
+
const files = (0, generator_1.generateInstanceFiles)({
|
|
133
|
+
name: 'CleanBrainHands',
|
|
134
|
+
selectedManifests: [brain, hands],
|
|
135
|
+
});
|
|
136
|
+
const pkgObj = JSON.parse(files['package.json']);
|
|
137
|
+
pkgObj.dependencies = {
|
|
138
|
+
'@sidurijs/core': `file:${getTarPath('@sidurijs/core')}`,
|
|
139
|
+
'@sidurijs/brain': `file:${getTarPath('@sidurijs/brain')}`,
|
|
140
|
+
'@sidurijs/hands': `file:${getTarPath('@sidurijs/hands')}`,
|
|
141
|
+
};
|
|
142
|
+
pkgObj.devDependencies = {
|
|
143
|
+
'@vxnus/siduri': `file:${getTarPath('@vxnus/siduri')}`,
|
|
144
|
+
};
|
|
145
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'package.json'), JSON.stringify(pkgObj, null, 2) + '\n');
|
|
146
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'siduri.config.json'), files['siduri.config.json']);
|
|
147
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'siduri.schema.json'), files['siduri.schema.json']);
|
|
148
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, '.env.example'), files['.env.example']);
|
|
149
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'README.md'), files['README.md']);
|
|
150
|
+
node_fs_1.default.writeFileSync(node_path_1.default.join(instanceDir, 'src/index.js'), files['src/index.js']);
|
|
151
|
+
(0, node_child_process_1.execSync)('npm install --no-audit --no-fund', { cwd: instanceDir, stdio: 'pipe' });
|
|
152
|
+
const doctorOutput = (0, node_child_process_1.execSync)(`node ${node_path_1.default.resolve(repoRoot, 'cli/dist/index.js')} doctor`, {
|
|
153
|
+
cwd: instanceDir,
|
|
154
|
+
env: {
|
|
155
|
+
...process.env,
|
|
156
|
+
OPENROUTER_API_KEY: 'test-key-clean',
|
|
157
|
+
ACTION_POLICY_SECRET: 'test-secret-clean',
|
|
158
|
+
},
|
|
159
|
+
encoding: 'utf8',
|
|
160
|
+
});
|
|
161
|
+
expect(doctorOutput).toContain('ACTION_POLICY_SECRET');
|
|
162
|
+
expect(doctorOutput).toContain('Result: PASS');
|
|
163
|
+
}, 60000);
|
|
164
|
+
});
|
|
165
|
+
});
|
package/dist/colors.d.ts
ADDED
package/dist/colors.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.colors = void 0;
|
|
4
|
+
exports.colors = {
|
|
5
|
+
cyan: '\u001b[36m',
|
|
6
|
+
dim: '\u001b[2m',
|
|
7
|
+
green: '\u001b[32m',
|
|
8
|
+
yellow: '\u001b[33m',
|
|
9
|
+
bold: '\u001b[1m',
|
|
10
|
+
reset: '\u001b[0m',
|
|
11
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.configureBehavior = configureBehavior;
|
|
7
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
8
|
+
async function configureBehavior(_context) {
|
|
9
|
+
const { personaMode } = await inquirer_1.default.prompt({
|
|
10
|
+
type: 'list',
|
|
11
|
+
name: 'personaMode',
|
|
12
|
+
message: 'Define base persona now or later?',
|
|
13
|
+
choices: [
|
|
14
|
+
{
|
|
15
|
+
name: 'Later (Start as a pure blank slate; evolve via interaction or add .self later)',
|
|
16
|
+
value: 'later',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
name: 'Now (Define archetype, ethos, and generate base .self persona asset)',
|
|
20
|
+
value: 'now',
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
default: 'later',
|
|
24
|
+
});
|
|
25
|
+
if (personaMode === 'later') {
|
|
26
|
+
return {
|
|
27
|
+
config: {
|
|
28
|
+
provider: 'active_self',
|
|
29
|
+
mode: 'blank_slate',
|
|
30
|
+
},
|
|
31
|
+
summary: {
|
|
32
|
+
Persona: 'Blank Slate (Evolve via interaction)',
|
|
33
|
+
Directives: 'Zero predeclared biases',
|
|
34
|
+
},
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
const personaAnswers = await inquirer_1.default.prompt([
|
|
38
|
+
{
|
|
39
|
+
type: 'input',
|
|
40
|
+
name: 'archetype',
|
|
41
|
+
message: 'Companion archetype or role:',
|
|
42
|
+
default: 'Knowledge Assistant & Research Partner',
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
type: 'input',
|
|
46
|
+
name: 'ethos',
|
|
47
|
+
message: 'Core ethos & speaking demeanor:',
|
|
48
|
+
default: 'Direct technical candor, thoughtful, concise, and loyal',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
type: 'input',
|
|
52
|
+
name: 'directive',
|
|
53
|
+
message: 'Primary behavioral directive or rule:',
|
|
54
|
+
default: 'Speak concisely and stay in character without sycophantic filler',
|
|
55
|
+
},
|
|
56
|
+
]);
|
|
57
|
+
const archetype = personaAnswers.archetype.trim() || 'Knowledge Assistant & Research Partner';
|
|
58
|
+
const ethos = personaAnswers.ethos.trim() || 'Direct technical candor, thoughtful, concise, and loyal';
|
|
59
|
+
const directive = personaAnswers.directive.trim() || 'Speak concisely and stay in character without sycophantic filler';
|
|
60
|
+
const selfPath = './assets/self/default.self';
|
|
61
|
+
return {
|
|
62
|
+
config: {
|
|
63
|
+
provider: 'active_self',
|
|
64
|
+
mode: 'custom',
|
|
65
|
+
archetype,
|
|
66
|
+
ethos,
|
|
67
|
+
directive,
|
|
68
|
+
selfPath,
|
|
69
|
+
},
|
|
70
|
+
summary: {
|
|
71
|
+
Persona: 'Configured (.self asset)',
|
|
72
|
+
Archetype: archetype,
|
|
73
|
+
Ethos: ethos,
|
|
74
|
+
'Self Asset': selfPath,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.configureBody = configureBody;
|
|
7
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
8
|
+
const colors_1 = require("../colors");
|
|
9
|
+
async function configureBody(_context) {
|
|
10
|
+
const { provider } = await inquirer_1.default.prompt({
|
|
11
|
+
type: 'list',
|
|
12
|
+
name: 'provider',
|
|
13
|
+
message: 'Body avatar provider?',
|
|
14
|
+
choices: [
|
|
15
|
+
{ name: 'Live2D Cubism (Interactive 2D avatar motion & expression state)', value: 'live2d' },
|
|
16
|
+
{ name: 'VRM 3D Humanoid (Interactive 3D avatar, VRoid / glTF)', value: 'vrm' },
|
|
17
|
+
{ name: 'None (Headless / No visual body)', value: 'none' },
|
|
18
|
+
],
|
|
19
|
+
});
|
|
20
|
+
if (provider === 'none') {
|
|
21
|
+
return {
|
|
22
|
+
config: { provider: 'none' },
|
|
23
|
+
summary: { Provider: 'None (Headless)' },
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
if (provider === 'vrm') {
|
|
27
|
+
const { modelSource, lookAtMode } = await inquirer_1.default.prompt([
|
|
28
|
+
{
|
|
29
|
+
type: 'input',
|
|
30
|
+
name: 'modelSource',
|
|
31
|
+
message: 'VRM Model path / URL (.vrm):',
|
|
32
|
+
default: './assets/body/default/model.vrm',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
type: 'list',
|
|
36
|
+
name: 'lookAtMode',
|
|
37
|
+
message: 'VRM Camera LookAt target behavior:',
|
|
38
|
+
choices: [
|
|
39
|
+
{ name: 'Camera (Look towards viewer)', value: 'camera' },
|
|
40
|
+
{ name: 'Cursor (Track mouse / touch cursor)', value: 'cursor' },
|
|
41
|
+
{ name: 'Head (Restricted head-only tracking)', value: 'head' },
|
|
42
|
+
{ name: 'None (Fixed gaze)', value: 'none' },
|
|
43
|
+
],
|
|
44
|
+
default: 'camera',
|
|
45
|
+
},
|
|
46
|
+
]);
|
|
47
|
+
const modelPath = modelSource.trim() || './assets/body/default/model.vrm';
|
|
48
|
+
const isHttpOrAbsolute = modelPath.startsWith('http://') || modelPath.startsWith('https://') || modelPath.startsWith('/');
|
|
49
|
+
const webModelUrl = isHttpOrAbsolute
|
|
50
|
+
? modelPath
|
|
51
|
+
: modelPath.startsWith('./')
|
|
52
|
+
? modelPath.slice(1)
|
|
53
|
+
: `/${modelPath}`;
|
|
54
|
+
return {
|
|
55
|
+
config: {
|
|
56
|
+
provider: 'vrm',
|
|
57
|
+
format: 'vrm',
|
|
58
|
+
modelPath,
|
|
59
|
+
modelUrl: webModelUrl,
|
|
60
|
+
initialExpression: 'neutral',
|
|
61
|
+
vrm: {
|
|
62
|
+
specVersion: 'auto',
|
|
63
|
+
lookAtMode: lookAtMode,
|
|
64
|
+
},
|
|
65
|
+
},
|
|
66
|
+
summary: {
|
|
67
|
+
Provider: 'VRM (3D Avatar)',
|
|
68
|
+
'Model Path': modelPath,
|
|
69
|
+
'LookAt Mode': lookAtMode,
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
// provider === 'live2d'
|
|
74
|
+
console.log(`\n ${colors_1.colors.yellow}⚠ Live2D Cubism Licensing & Runtime Notice:${colors_1.colors.reset}`);
|
|
75
|
+
console.log(` • ${colors_1.colors.dim}Proprietary Core:${colors_1.colors.reset} Live2D Cubism Core is proprietary software of Live2D Inc.`);
|
|
76
|
+
console.log(` • ${colors_1.colors.dim}Indie / Small Biz:${colors_1.colors.reset} Free & royalty-free for entities with annual revenue < 10M JPY (~$67k USD).`);
|
|
77
|
+
console.log(` • ${colors_1.colors.dim}Commercial License:${colors_1.colors.reset} Entities exceeding this threshold require a Publication License from Live2D Inc.`);
|
|
78
|
+
console.log(` • ${colors_1.colors.dim}Runtime Download:${colors_1.colors.reset} Clients fetch live2dcubismcore.min.js directly from Live2D CDN at runtime.\n`);
|
|
79
|
+
const { modelSource } = await inquirer_1.default.prompt([
|
|
80
|
+
{
|
|
81
|
+
type: 'input',
|
|
82
|
+
name: 'modelSource',
|
|
83
|
+
message: 'Live2D Model path / URL (.model3.json):',
|
|
84
|
+
default: './assets/body/default/model.model3.json',
|
|
85
|
+
},
|
|
86
|
+
]);
|
|
87
|
+
const modelPath = modelSource.trim() || './assets/body/default/model.model3.json';
|
|
88
|
+
const isHttpOrAbsolute = modelPath.startsWith('http://') || modelPath.startsWith('https://') || modelPath.startsWith('/');
|
|
89
|
+
const webModelUrl = isHttpOrAbsolute
|
|
90
|
+
? modelPath
|
|
91
|
+
: modelPath.startsWith('./')
|
|
92
|
+
? modelPath.slice(1)
|
|
93
|
+
: `/${modelPath}`;
|
|
94
|
+
return {
|
|
95
|
+
config: {
|
|
96
|
+
provider: 'live2d',
|
|
97
|
+
format: 'live2d',
|
|
98
|
+
modelPath,
|
|
99
|
+
modelUrl: webModelUrl,
|
|
100
|
+
initialExpression: 'neutral',
|
|
101
|
+
},
|
|
102
|
+
summary: {
|
|
103
|
+
Provider: 'Live2D',
|
|
104
|
+
'Model Path': modelPath,
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { OrganConfiguratorContext, OrganConfigurationResult } from './types';
|
|
2
|
+
import { ModelProvider } from '../providers/openrouter';
|
|
3
|
+
export interface BrainConfiguratorOptions {
|
|
4
|
+
modelProvider?: ModelProvider;
|
|
5
|
+
}
|
|
6
|
+
export declare function configureBrain(_context: OrganConfiguratorContext, options?: BrainConfiguratorOptions): Promise<OrganConfigurationResult>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.configureBrain = configureBrain;
|
|
7
|
+
const inquirer_1 = __importDefault(require("inquirer"));
|
|
8
|
+
const openrouter_1 = require("../providers/openrouter");
|
|
9
|
+
async function configureBrain(_context, options = {}) {
|
|
10
|
+
const modelProvider = options.modelProvider || new openrouter_1.OpenRouterModelProvider();
|
|
11
|
+
while (true) {
|
|
12
|
+
const { provider } = await inquirer_1.default.prompt({
|
|
13
|
+
type: 'list',
|
|
14
|
+
name: 'provider',
|
|
15
|
+
message: 'Brain provider?',
|
|
16
|
+
choices: [
|
|
17
|
+
{ name: 'OpenRouter (Managed Routing & Broad Model Access)', value: 'openrouter' },
|
|
18
|
+
{ name: 'OpenAI-compatible / Custom endpoint (Ollama, vLLM, Azure, OpenAI)', value: 'openai-compatible' },
|
|
19
|
+
],
|
|
20
|
+
});
|
|
21
|
+
if (provider === 'openrouter') {
|
|
22
|
+
const selection = await (0, openrouter_1.promptOpenRouterModelSelection)(modelProvider);
|
|
23
|
+
if (selection.type === 'switch_provider') {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (selection.type === 'cancel') {
|
|
27
|
+
throw new Error('Brain configuration cancelled.');
|
|
28
|
+
}
|
|
29
|
+
const modelId = selection.modelId;
|
|
30
|
+
return {
|
|
31
|
+
config: {
|
|
32
|
+
provider: 'openrouter',
|
|
33
|
+
model: modelId,
|
|
34
|
+
apiKeyEnv: 'OPENROUTER_API_KEY',
|
|
35
|
+
},
|
|
36
|
+
summary: {
|
|
37
|
+
Provider: 'OpenRouter',
|
|
38
|
+
Model: modelId,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
if (provider === 'openai-compatible') {
|
|
43
|
+
const answers = await inquirer_1.default.prompt([
|
|
44
|
+
{
|
|
45
|
+
type: 'input',
|
|
46
|
+
name: 'baseUrl',
|
|
47
|
+
message: 'Endpoint Base URL:',
|
|
48
|
+
default: 'https://api.openai.com/v1',
|
|
49
|
+
validate: (v) => v.trim().length > 0 || 'Base URL is required.',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
type: 'input',
|
|
53
|
+
name: 'model',
|
|
54
|
+
message: 'Model ID (e.g. gpt-4o, llama3, mistral):',
|
|
55
|
+
default: 'gpt-4o',
|
|
56
|
+
validate: (v) => v.trim().length > 0 || 'Model ID is required.',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
type: 'input',
|
|
60
|
+
name: 'apiKeyEnv',
|
|
61
|
+
message: 'API Key environment variable name:',
|
|
62
|
+
default: 'OPENAI_COMPATIBLE_API_KEY',
|
|
63
|
+
},
|
|
64
|
+
]);
|
|
65
|
+
return {
|
|
66
|
+
config: {
|
|
67
|
+
provider: 'openai-compatible',
|
|
68
|
+
model: answers.model.trim(),
|
|
69
|
+
baseUrl: answers.baseUrl.trim(),
|
|
70
|
+
apiKeyEnv: answers.apiKeyEnv.trim() || 'OPENAI_COMPATIBLE_API_KEY',
|
|
71
|
+
},
|
|
72
|
+
summary: {
|
|
73
|
+
Provider: 'OpenAI-compatible',
|
|
74
|
+
Model: answers.model.trim(),
|
|
75
|
+
'Base URL': answers.baseUrl.trim(),
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|