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,69 @@
|
|
|
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_path_1 = __importDefault(require("node:path"));
|
|
7
|
+
const discovery_1 = require("./discovery");
|
|
8
|
+
const generator_1 = require("./generator");
|
|
9
|
+
describe('Discovery & Dynamic Composition System Tests (Phase 3)', () => {
|
|
10
|
+
const rootOrgansDir = node_path_1.default.resolve(__dirname, '../../packages/organs');
|
|
11
|
+
test('Registry successfully discovers all 11 organ packages from workspace', () => {
|
|
12
|
+
const registry = discovery_1.OrganRegistry.discover();
|
|
13
|
+
const manifests = registry.getAll();
|
|
14
|
+
expect(manifests.length).toBe(11);
|
|
15
|
+
const organTypes = registry.getAvailableOrganTypes().sort();
|
|
16
|
+
expect(organTypes).toEqual([
|
|
17
|
+
'archive',
|
|
18
|
+
'behavior',
|
|
19
|
+
'body',
|
|
20
|
+
'brain',
|
|
21
|
+
'ear',
|
|
22
|
+
'hands',
|
|
23
|
+
'knowledge',
|
|
24
|
+
'mouth',
|
|
25
|
+
'observation',
|
|
26
|
+
'vision',
|
|
27
|
+
'voice',
|
|
28
|
+
]);
|
|
29
|
+
});
|
|
30
|
+
test('Generates valid instances from discovered manifests', () => {
|
|
31
|
+
const registry = discovery_1.OrganRegistry.discover();
|
|
32
|
+
// Test Brain + Hands + Vision
|
|
33
|
+
const brain = registry.get('brain');
|
|
34
|
+
const hands = registry.get('hands');
|
|
35
|
+
const vision = registry.get('vision');
|
|
36
|
+
const files = (0, generator_1.generateInstanceFiles)({
|
|
37
|
+
name: 'robotics-agent',
|
|
38
|
+
selectedManifests: [brain, hands, vision],
|
|
39
|
+
});
|
|
40
|
+
const pkg = JSON.parse(files['package.json']);
|
|
41
|
+
expect(Object.keys(pkg.dependencies).sort()).toEqual([
|
|
42
|
+
'@sidurijs/brain',
|
|
43
|
+
'@sidurijs/core',
|
|
44
|
+
'@sidurijs/hands',
|
|
45
|
+
'@sidurijs/vision',
|
|
46
|
+
]);
|
|
47
|
+
const schema = JSON.parse(files['siduri.schema.json']);
|
|
48
|
+
expect(Object.keys(schema.properties.organs.properties).sort()).toEqual(['brain', 'hands', 'vision']);
|
|
49
|
+
const config = JSON.parse(files['siduri.config.json']);
|
|
50
|
+
expect(Object.keys(config.organs).sort()).toEqual(['brain', 'hands', 'vision']);
|
|
51
|
+
expect(files['.env.example']).toContain('OPENROUTER_API_KEY');
|
|
52
|
+
expect(files['.env.example']).toContain('ACTION_POLICY_SECRET');
|
|
53
|
+
expect(files['.env.example']).not.toContain('DATABASE_URL');
|
|
54
|
+
});
|
|
55
|
+
test('Validates manifest contract integrity during discovery', () => {
|
|
56
|
+
expect(() => {
|
|
57
|
+
const invalidRegistry = new discovery_1.OrganRegistry();
|
|
58
|
+
invalidRegistry.register({});
|
|
59
|
+
}).toThrow(/Invalid manifest/);
|
|
60
|
+
});
|
|
61
|
+
test('Fallback to builtin organ manifests when scanning non-existent search roots', () => {
|
|
62
|
+
const registry = discovery_1.OrganRegistry.discover(['/non-existent-directory/empty']);
|
|
63
|
+
const manifests = registry.getAll();
|
|
64
|
+
expect(manifests.length).toBe(11);
|
|
65
|
+
expect(registry.get('brain')).toBeDefined();
|
|
66
|
+
expect(registry.get('archive')).toBeDefined();
|
|
67
|
+
expect(registry.get('mouth')).toBeDefined();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
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 doctor_1 = require("./doctor");
|
|
9
|
+
const db_1 = require("./db");
|
|
10
|
+
const generator_1 = require("./generator");
|
|
11
|
+
const discovery_1 = require("./discovery");
|
|
12
|
+
describe('Phase 4: Diagnostics and Database Provisioning Tests', () => {
|
|
13
|
+
const rootOrgansDir = node_path_1.default.resolve(__dirname, '../../packages/organs');
|
|
14
|
+
const registry = discovery_1.OrganRegistry.discover();
|
|
15
|
+
const brain = registry.get('brain');
|
|
16
|
+
const hands = registry.get('hands');
|
|
17
|
+
const testTempRoot = node_path_1.default.resolve(__dirname, '../temp-phase4-test');
|
|
18
|
+
beforeAll(() => {
|
|
19
|
+
if (node_fs_1.default.existsSync(testTempRoot)) {
|
|
20
|
+
node_fs_1.default.rmSync(testTempRoot, { recursive: true, force: true });
|
|
21
|
+
}
|
|
22
|
+
node_fs_1.default.mkdirSync(testTempRoot, { recursive: true });
|
|
23
|
+
});
|
|
24
|
+
afterAll(() => {
|
|
25
|
+
if (node_fs_1.default.existsSync(testTempRoot)) {
|
|
26
|
+
node_fs_1.default.rmSync(testTempRoot, { recursive: true, force: true });
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
test('Test Matrix A: Brain only instance doctor and db push', async () => {
|
|
30
|
+
const instanceDir = node_path_1.default.join(testTempRoot, 'brain-only');
|
|
31
|
+
node_fs_1.default.mkdirSync(node_path_1.default.join(instanceDir, 'src'), { recursive: true });
|
|
32
|
+
const files = (0, generator_1.generateInstanceFiles)({
|
|
33
|
+
name: 'BrainOnlyInstance',
|
|
34
|
+
selectedManifests: [brain],
|
|
35
|
+
});
|
|
36
|
+
for (const [name, content] of Object.entries(files)) {
|
|
37
|
+
if (typeof content === 'string') {
|
|
38
|
+
const fullPath = node_path_1.default.join(instanceDir, name);
|
|
39
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(fullPath), { recursive: true });
|
|
40
|
+
node_fs_1.default.writeFileSync(fullPath, content);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const doctorPass = await (0, doctor_1.runDoctor)({
|
|
44
|
+
projectDir: instanceDir,
|
|
45
|
+
env: { OPENROUTER_API_KEY: 'test-key-123' },
|
|
46
|
+
});
|
|
47
|
+
expect(doctorPass.passed).toBe(true);
|
|
48
|
+
const dbResult = await (0, db_1.runDbPush)({ projectDir: instanceDir });
|
|
49
|
+
expect(dbResult.status).toBe('NOOP');
|
|
50
|
+
expect(dbResult.message).toContain('SQLite manages schema natively.');
|
|
51
|
+
});
|
|
52
|
+
});
|
package/dist/doctor.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface DoctorCheckResult {
|
|
2
|
+
category: 'Environment' | 'Services' | 'Database' | 'Health Probe' | 'Configuration';
|
|
3
|
+
name: string;
|
|
4
|
+
status: 'PASS' | 'FAIL' | 'OPTIONAL_MISSING' | 'SKIPPED';
|
|
5
|
+
organName?: string;
|
|
6
|
+
message?: string;
|
|
7
|
+
remediation?: string;
|
|
8
|
+
}
|
|
9
|
+
export interface DoctorReport {
|
|
10
|
+
instanceName: string;
|
|
11
|
+
configuredOrgans: string[];
|
|
12
|
+
results: DoctorCheckResult[];
|
|
13
|
+
passed: boolean;
|
|
14
|
+
}
|
|
15
|
+
export interface DoctorOptions {
|
|
16
|
+
projectDir?: string;
|
|
17
|
+
env?: Record<string, string | undefined>;
|
|
18
|
+
}
|
|
19
|
+
export declare function runDoctor(options?: DoctorOptions): Promise<DoctorReport>;
|
package/dist/doctor.js
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
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.runDoctor = runDoctor;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const discovery_1 = require("./discovery");
|
|
10
|
+
const schema_validator_1 = require("./schema-validator");
|
|
11
|
+
async function runDoctor(options = {}) {
|
|
12
|
+
const projectDir = options.projectDir ? node_path_1.default.resolve(options.projectDir) : process.cwd();
|
|
13
|
+
const configPath = node_path_1.default.join(projectDir, 'siduri.config.json');
|
|
14
|
+
if (!node_fs_1.default.existsSync(configPath)) {
|
|
15
|
+
throw new Error(`siduri.config.json not found at ${projectDir}. Make sure you are in a Siduri instance directory.`);
|
|
16
|
+
}
|
|
17
|
+
// Load .env if present in projectDir
|
|
18
|
+
const envFile = node_path_1.default.join(projectDir, '.env');
|
|
19
|
+
const fileEnv = {};
|
|
20
|
+
if (node_fs_1.default.existsSync(envFile)) {
|
|
21
|
+
const lines = node_fs_1.default.readFileSync(envFile, 'utf8').split('\n');
|
|
22
|
+
for (const line of lines) {
|
|
23
|
+
const trimmed = line.trim();
|
|
24
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
25
|
+
continue;
|
|
26
|
+
const eqIdx = trimmed.indexOf('=');
|
|
27
|
+
if (eqIdx !== -1) {
|
|
28
|
+
const key = trimmed.slice(0, eqIdx).trim();
|
|
29
|
+
let val = trimmed.slice(eqIdx + 1).trim();
|
|
30
|
+
if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) {
|
|
31
|
+
val = val.slice(1, -1);
|
|
32
|
+
}
|
|
33
|
+
if (val) {
|
|
34
|
+
fileEnv[key] = val;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const effectiveEnv = {
|
|
40
|
+
...process.env,
|
|
41
|
+
...fileEnv,
|
|
42
|
+
...(options.env || {}),
|
|
43
|
+
};
|
|
44
|
+
const config = JSON.parse(node_fs_1.default.readFileSync(configPath, 'utf8'));
|
|
45
|
+
const configuredOrgansMap = config.organs || {};
|
|
46
|
+
const organKeys = Object.keys(configuredOrgansMap);
|
|
47
|
+
const registry = discovery_1.OrganRegistry.discover([
|
|
48
|
+
node_path_1.default.join(projectDir, 'node_modules/@siduri-x'),
|
|
49
|
+
node_path_1.default.resolve(__dirname, '../../packages/organs'),
|
|
50
|
+
node_path_1.default.resolve(process.cwd(), 'packages/organs'),
|
|
51
|
+
]);
|
|
52
|
+
const selectedManifests = [];
|
|
53
|
+
const results = [];
|
|
54
|
+
// 0. Schema Validation Check
|
|
55
|
+
const schemaPath = node_path_1.default.join(projectDir, 'siduri.schema.json');
|
|
56
|
+
if (node_fs_1.default.existsSync(schemaPath)) {
|
|
57
|
+
try {
|
|
58
|
+
const schema = JSON.parse(node_fs_1.default.readFileSync(schemaPath, 'utf8'));
|
|
59
|
+
(0, schema_validator_1.validateCompanionConfig)(config, schema);
|
|
60
|
+
results.push({
|
|
61
|
+
category: 'Configuration',
|
|
62
|
+
name: 'Schema Conformance',
|
|
63
|
+
status: 'PASS',
|
|
64
|
+
message: 'siduri.config.json conforms to siduri.schema.json',
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
catch (err) {
|
|
68
|
+
results.push({
|
|
69
|
+
category: 'Configuration',
|
|
70
|
+
name: 'Schema Conformance',
|
|
71
|
+
status: 'FAIL',
|
|
72
|
+
message: err.message,
|
|
73
|
+
remediation: 'Update siduri.config.json to conform to the generated schema.',
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
// 1. Resolve manifests for selected organs
|
|
78
|
+
for (const organKey of organKeys) {
|
|
79
|
+
const m = registry.get(organKey) || registry.getAll().find((item) => item.configKey === organKey || item.organType === organKey);
|
|
80
|
+
if (!m) {
|
|
81
|
+
results.push({
|
|
82
|
+
category: 'Environment',
|
|
83
|
+
name: `Manifest resolution: ${organKey}`,
|
|
84
|
+
status: 'FAIL',
|
|
85
|
+
message: `Could not resolve package manifest for configured organ '${organKey}'.`,
|
|
86
|
+
remediation: `Ensure @sidurijs/${organKey} is installed in package.json and npm install has been run.`,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
selectedManifests.push(m);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
// 2. Check Environment Variables (deduplicated)
|
|
94
|
+
const checkedEnvVars = new Set();
|
|
95
|
+
for (const m of selectedManifests) {
|
|
96
|
+
for (const envVar of m.environment || []) {
|
|
97
|
+
if (checkedEnvVars.has(envVar.name))
|
|
98
|
+
continue;
|
|
99
|
+
checkedEnvVars.add(envVar.name);
|
|
100
|
+
const val = effectiveEnv[envVar.name];
|
|
101
|
+
const isMissing = !val || val.trim() === '';
|
|
102
|
+
if (isMissing) {
|
|
103
|
+
if (envVar.required) {
|
|
104
|
+
results.push({
|
|
105
|
+
category: 'Environment',
|
|
106
|
+
name: envVar.name,
|
|
107
|
+
status: 'FAIL',
|
|
108
|
+
organName: m.name,
|
|
109
|
+
message: `Missing required environment variable '${envVar.name}'.`,
|
|
110
|
+
remediation: `Set ${envVar.name} in .env or your shell environment. ${envVar.description || ''}`,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
results.push({
|
|
115
|
+
category: 'Environment',
|
|
116
|
+
name: envVar.name,
|
|
117
|
+
status: 'OPTIONAL_MISSING',
|
|
118
|
+
organName: m.name,
|
|
119
|
+
message: `Optional variable '${envVar.name}' not set.`,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
results.push({
|
|
125
|
+
category: 'Environment',
|
|
126
|
+
name: envVar.name,
|
|
127
|
+
status: 'PASS',
|
|
128
|
+
organName: m.name,
|
|
129
|
+
message: 'Configured',
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// 3. Check External Services
|
|
135
|
+
for (const m of selectedManifests) {
|
|
136
|
+
for (const service of m.services || []) {
|
|
137
|
+
const organConf = configuredOrgansMap[m.configKey] || configuredOrgansMap[m.organType];
|
|
138
|
+
const configuredUrl = organConf?.baseUrl || organConf?.url || effectiveEnv[`${service.name.toUpperCase()}_URL`];
|
|
139
|
+
if (configuredUrl && typeof configuredUrl === 'string' && configuredUrl.startsWith('http')) {
|
|
140
|
+
try {
|
|
141
|
+
const controller = new AbortController();
|
|
142
|
+
const timer = setTimeout(() => controller.abort(), 1000);
|
|
143
|
+
await fetch(configuredUrl, { signal: controller.signal }).catch(() => { });
|
|
144
|
+
clearTimeout(timer);
|
|
145
|
+
results.push({
|
|
146
|
+
category: 'Services',
|
|
147
|
+
name: `${service.name} (${m.organType})`,
|
|
148
|
+
status: 'PASS',
|
|
149
|
+
organName: m.name,
|
|
150
|
+
message: `Endpoint configured: ${configuredUrl}`,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
catch {
|
|
154
|
+
results.push({
|
|
155
|
+
category: 'Services',
|
|
156
|
+
name: `${service.name} (${m.organType})`,
|
|
157
|
+
status: 'OPTIONAL_MISSING',
|
|
158
|
+
organName: m.name,
|
|
159
|
+
message: `Service at ${configuredUrl} is not responding (optional local service)`,
|
|
160
|
+
remediation: `Start ${service.name} or allow auto-download if applicable.`,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
results.push({
|
|
166
|
+
category: 'Services',
|
|
167
|
+
name: `${service.name} (${m.organType})`,
|
|
168
|
+
status: 'PASS',
|
|
169
|
+
organName: m.name,
|
|
170
|
+
message: 'Service requirement declared (managed locally)',
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
// 4. Database Check (only if an organ declares database requirement)
|
|
176
|
+
const dbOrgan = selectedManifests.find((m) => m.database !== null && m.database !== undefined);
|
|
177
|
+
if (dbOrgan) {
|
|
178
|
+
try {
|
|
179
|
+
const testDbPath = node_path_1.default.join(projectDir, '.doctor-sqlite-test.tmp');
|
|
180
|
+
node_fs_1.default.writeFileSync(testDbPath, '');
|
|
181
|
+
node_fs_1.default.unlinkSync(testDbPath);
|
|
182
|
+
results.push({
|
|
183
|
+
category: 'Database',
|
|
184
|
+
name: 'SQLite Database',
|
|
185
|
+
status: 'PASS',
|
|
186
|
+
organName: dbOrgan.name,
|
|
187
|
+
message: 'Local storage directory writeable; SQLite operational',
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
results.push({
|
|
192
|
+
category: 'Database',
|
|
193
|
+
name: 'SQLite Database',
|
|
194
|
+
status: 'FAIL',
|
|
195
|
+
organName: dbOrgan.name,
|
|
196
|
+
message: `Storage directory write check failed: ${err.message}`,
|
|
197
|
+
remediation: 'Verify write permissions for the companion directory.',
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
else {
|
|
202
|
+
results.push({
|
|
203
|
+
category: 'Database',
|
|
204
|
+
name: 'SQLite Database',
|
|
205
|
+
status: 'SKIPPED',
|
|
206
|
+
message: 'Not required by current composition',
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
// 5. Organ Health Probes
|
|
210
|
+
for (const m of selectedManifests) {
|
|
211
|
+
if (m.healthCheck) {
|
|
212
|
+
try {
|
|
213
|
+
// Resolve package entrypoint
|
|
214
|
+
const candidateEntrypoints = [
|
|
215
|
+
node_path_1.default.resolve(projectDir, 'node_modules', m.name, m.entrypoint),
|
|
216
|
+
node_path_1.default.resolve(__dirname, '../../packages/organs', m.organType, m.entrypoint),
|
|
217
|
+
node_path_1.default.resolve(process.cwd(), 'packages/organs', m.organType, m.entrypoint),
|
|
218
|
+
];
|
|
219
|
+
const entryPath = candidateEntrypoints.find((p) => node_fs_1.default.existsSync(p));
|
|
220
|
+
if (entryPath) {
|
|
221
|
+
const mod = require(entryPath);
|
|
222
|
+
const probeFn = mod[m.healthCheck];
|
|
223
|
+
if (typeof probeFn === 'function') {
|
|
224
|
+
const organConf = configuredOrgansMap[m.configKey] || configuredOrgansMap[m.organType];
|
|
225
|
+
const probeRes = await probeFn({ config: organConf, env: effectiveEnv });
|
|
226
|
+
if (probeRes && probeRes.ok === false) {
|
|
227
|
+
results.push({
|
|
228
|
+
category: 'Health Probe',
|
|
229
|
+
name: `${m.displayName} Probe`,
|
|
230
|
+
status: 'FAIL',
|
|
231
|
+
organName: m.name,
|
|
232
|
+
message: probeRes.message || 'Health probe check failed',
|
|
233
|
+
remediation: `Check ${m.displayName} configuration in siduri.config.json.`,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
results.push({
|
|
238
|
+
category: 'Health Probe',
|
|
239
|
+
name: `${m.displayName} Probe`,
|
|
240
|
+
status: 'PASS',
|
|
241
|
+
organName: m.name,
|
|
242
|
+
message: probeRes?.message || 'Operational',
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
catch (err) {
|
|
249
|
+
results.push({
|
|
250
|
+
category: 'Health Probe',
|
|
251
|
+
name: `${m.displayName} Probe`,
|
|
252
|
+
status: 'FAIL',
|
|
253
|
+
organName: m.name,
|
|
254
|
+
message: `Probe invocation failed: ${err.message}`,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
const passed = results.every((r) => r.status !== 'FAIL');
|
|
260
|
+
return {
|
|
261
|
+
instanceName: config.name || 'Siduri Instance',
|
|
262
|
+
configuredOrgans: organKeys,
|
|
263
|
+
results,
|
|
264
|
+
passed,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { OrganManifest } from './manifest';
|
|
2
|
+
export interface GeneratedInstanceFiles {
|
|
3
|
+
'package.json': string;
|
|
4
|
+
'siduri.config.json': string;
|
|
5
|
+
'siduri.schema.json': string;
|
|
6
|
+
'.env.example': string;
|
|
7
|
+
'README.md': string;
|
|
8
|
+
'src/index.js': string;
|
|
9
|
+
'public/index.html': string;
|
|
10
|
+
createAssetsBodyDir?: boolean;
|
|
11
|
+
createAssetsDirs?: string[];
|
|
12
|
+
[key: string]: string | boolean | string[] | undefined;
|
|
13
|
+
}
|
|
14
|
+
export interface InstanceGeneratorOptions {
|
|
15
|
+
name: string;
|
|
16
|
+
id?: string;
|
|
17
|
+
selectedManifests: OrganManifest[];
|
|
18
|
+
organConfigs?: Record<string, any>;
|
|
19
|
+
coreVersion?: string;
|
|
20
|
+
cliVersion?: string;
|
|
21
|
+
localPath?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function generateInstanceFiles(options: InstanceGeneratorOptions): GeneratedInstanceFiles;
|