fraim-framework 2.0.24 → 2.0.26
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/.github/workflows/deploy-fraim.yml +3 -1
- package/dist/src/fraim/config-loader.js +19 -11
- package/dist/src/fraim/setup-wizard.js +28 -1
- package/dist/src/fraim/types.js +11 -0
- package/dist/src/fraim-mcp-server.js +22 -11
- package/dist/src/utils/git-utils.js +1 -1
- package/dist/tests/test-cli.js +169 -0
- package/dist/tests/test-first-run-journey.js +108 -0
- package/dist/tests/test-genericization.js +66 -0
- package/{test-prep-issue.ts → dist/tests/test-prep-issue.js} +93 -101
- package/{test-standalone.ts → dist/tests/test-standalone.js} +149 -161
- package/dist/tests/test-user-journey.js +231 -0
- package/dist/tests/test-utils.js +96 -0
- package/{test-wizard.ts → dist/tests/test-wizard.js} +71 -81
- package/package.json +9 -5
- package/registry/rules/architecture.md +1 -1
- package/registry/scripts/code-quality-check.sh +5 -4
- package/registry/scripts/evaluate-code-quality.ts +36 -0
- package/registry/scripts/{validate-coverage.ts → validate-test-coverage.ts} +39 -39
- package/registry/scripts/verify-test-coverage.ts +36 -0
- package/registry/templates/bootstrap/ARCHITECTURE-TEMPLATE.md +53 -0
- package/registry/templates/bootstrap/CODE-QUALITY-REPORT-TEMPLATE.md +37 -0
- package/registry/templates/bootstrap/TEST-COVERAGE-REPORT-TEMPLATE.md +35 -0
- package/registry/templates/business-development/PRICING-STRATEGY-TEMPLATE.md +126 -0
- package/registry/workflows/bootstrap/create-architecture.md +13 -12
- package/registry/workflows/bootstrap/evaluate-code-quality.md +30 -0
- package/registry/workflows/bootstrap/verify-test-coverage.md +31 -0
- package/registry/workflows/business-development/price-product.md +325 -0
- package/tsconfig.json +4 -4
- package/test-cli.ts +0 -155
- package/test-first-run-journey.ts +0 -122
- package/test-genericization.ts +0 -74
- package/test-user-journey.ts +0 -244
- package/test-utils.ts +0 -120
|
@@ -1,101 +1,93 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
.replace(
|
|
24
|
-
.replace(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
if (result.status !== 0) {
|
|
35
|
-
throw new Error(`Script failed to run (status ${result.status}). Stderr: ${result.stderr}`);
|
|
36
|
-
}
|
|
37
|
-
return result.stdout.trim();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
runParsingLogic(nodeScript, {
|
|
67
|
-
}, /Script failed/);
|
|
68
|
-
console.log(' ✅
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
testFn
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
if (require.main === module) {
|
|
95
|
-
runTests(testCases, async (testCase) => {
|
|
96
|
-
if (testCase.testFn) {
|
|
97
|
-
return await testCase.testFn();
|
|
98
|
-
}
|
|
99
|
-
return false;
|
|
100
|
-
}, 'Prep Issue Script Tests');
|
|
101
|
-
}
|
|
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 fs_1 = __importDefault(require("fs"));
|
|
7
|
+
const path_1 = __importDefault(require("path"));
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
9
|
+
const assert_1 = __importDefault(require("assert"));
|
|
10
|
+
const test_utils_1 = require("./test-utils");
|
|
11
|
+
// Path to the shell script
|
|
12
|
+
const SCRIPT_PATH = path_1.default.join(__dirname, '../registry/scripts/prep-issue.sh');
|
|
13
|
+
function extractNodeScript() {
|
|
14
|
+
const content = fs_1.default.readFileSync(SCRIPT_PATH, 'utf8');
|
|
15
|
+
// Regex to capture the content inside NODE_SCRIPT="..."
|
|
16
|
+
const match = content.match(/NODE_SCRIPT="([\s\S]*?)"/);
|
|
17
|
+
if (!match || !match[1]) {
|
|
18
|
+
throw new Error('Could not find NODE_SCRIPT variable in prep-issue.sh');
|
|
19
|
+
}
|
|
20
|
+
// Unescape characters that are escaped in Bash double-quoted string
|
|
21
|
+
// specifically: \` \$ \" \\
|
|
22
|
+
return match[1]
|
|
23
|
+
.replace(/\\`/g, '`')
|
|
24
|
+
.replace(/\\\$/g, '$')
|
|
25
|
+
.replace(/\\"/g, '"')
|
|
26
|
+
.replace(/\\\\/g, '\\');
|
|
27
|
+
}
|
|
28
|
+
function runParsingLogic(script, config) {
|
|
29
|
+
const input = JSON.stringify(config);
|
|
30
|
+
const result = (0, child_process_1.spawnSync)('node', ['-e', script], {
|
|
31
|
+
input,
|
|
32
|
+
encoding: 'utf-8'
|
|
33
|
+
});
|
|
34
|
+
if (result.status !== 0) {
|
|
35
|
+
throw new Error(`Script failed to run (status ${result.status}). Stderr: ${result.stderr}`);
|
|
36
|
+
}
|
|
37
|
+
return result.stdout.trim();
|
|
38
|
+
}
|
|
39
|
+
async function verifyPrepIssueConfigParsing() {
|
|
40
|
+
console.log(' 🔍 Verifying prep-issue.sh config parsing logic...');
|
|
41
|
+
try {
|
|
42
|
+
// 1. Verify script exists
|
|
43
|
+
assert_1.default.ok(fs_1.default.existsSync(SCRIPT_PATH), `prep-issue.sh not found at ${SCRIPT_PATH}`);
|
|
44
|
+
// 2. Extract the Node.js parsing logic
|
|
45
|
+
const nodeScript = extractNodeScript();
|
|
46
|
+
assert_1.default.ok(nodeScript.length > 0, 'Extracted script is empty');
|
|
47
|
+
console.log(' ✅ Extracted Node.js parsing logic');
|
|
48
|
+
// 3. Test Valid Configuration
|
|
49
|
+
const validConfig = {
|
|
50
|
+
repository: {
|
|
51
|
+
owner: 'test-owner',
|
|
52
|
+
name: 'test-repo',
|
|
53
|
+
url: 'https://github.com/test-owner/test-repo.git'
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
const output = runParsingLogic(nodeScript, validConfig);
|
|
57
|
+
assert_1.default.strictEqual(output, 'test-owner:test-repo:https://github.com/test-owner/test-repo.git');
|
|
58
|
+
console.log(' ✅ Valid config parsed correctly');
|
|
59
|
+
// 4. Test Missing Config
|
|
60
|
+
assert_1.default.throws(() => {
|
|
61
|
+
runParsingLogic(nodeScript, { name: 'Create-Project' });
|
|
62
|
+
}, /Script failed/);
|
|
63
|
+
console.log(' ✅ Missing config correctly failed');
|
|
64
|
+
// 5. Test Incomplete Config
|
|
65
|
+
assert_1.default.throws(() => {
|
|
66
|
+
runParsingLogic(nodeScript, { repository: { owner: 'test-owner' } });
|
|
67
|
+
}, /Script failed/);
|
|
68
|
+
console.log(' ✅ Incomplete config correctly failed');
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
console.error(' ❌ Test failed:', error);
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Define test cases
|
|
77
|
+
const testCases = [
|
|
78
|
+
{
|
|
79
|
+
name: 'Prep Issue Config Parsing',
|
|
80
|
+
description: 'Verifies correct parsing of repo config from .fraim/config.json',
|
|
81
|
+
testFn: verifyPrepIssueConfigParsing,
|
|
82
|
+
tags: ['unit', 'scripts']
|
|
83
|
+
}
|
|
84
|
+
];
|
|
85
|
+
// Run strict check if executed directly
|
|
86
|
+
if (require.main === module) {
|
|
87
|
+
(0, test_utils_1.runTests)(testCases, async (testCase) => {
|
|
88
|
+
if (testCase.testFn) {
|
|
89
|
+
return await testCase.testFn();
|
|
90
|
+
}
|
|
91
|
+
return false;
|
|
92
|
+
}, 'Prep Issue Script Tests');
|
|
93
|
+
}
|
|
@@ -1,161 +1,149 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
let
|
|
16
|
-
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
try {
|
|
23
|
-
// 1. Seed the test API key in the database
|
|
24
|
-
dbService = new FraimDbService();
|
|
25
|
-
await dbService.connect();
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
await
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const testCases: FraimStandaloneTestCase[] = [
|
|
153
|
-
{
|
|
154
|
-
name: 'Fraim Standalone Server Integration',
|
|
155
|
-
description: 'Tests server startup, public health check, authenticated MCP access, usage logging, and admin API',
|
|
156
|
-
testFunction: testServerStartsAndResponds,
|
|
157
|
-
tags: ['smoke', 'fraim']
|
|
158
|
-
}
|
|
159
|
-
];
|
|
160
|
-
|
|
161
|
-
runTests(testCases, runFraimTest, 'Fraim Standalone Server');
|
|
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_child_process_1 = require("node:child_process");
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const db_service_js_1 = require("../src/fraim/db-service.js");
|
|
9
|
+
const test_utils_1 = require("./test-utils");
|
|
10
|
+
const node_assert_1 = __importDefault(require("node:assert"));
|
|
11
|
+
const tree_kill_1 = __importDefault(require("tree-kill"));
|
|
12
|
+
const path_1 = __importDefault(require("path"));
|
|
13
|
+
async function testServerStartsAndResponds() {
|
|
14
|
+
console.log(' 🚀 Testing Fraim Standalone Server...');
|
|
15
|
+
let fraimProcess;
|
|
16
|
+
let dbService;
|
|
17
|
+
const PORT = 10001; // Use a different port to avoid conflicts
|
|
18
|
+
const TEST_API_KEY = 'test-fraim-key-integration';
|
|
19
|
+
const TEST_ADMIN_KEY = 'test-admin-key-integration';
|
|
20
|
+
const TEST_ADMIN_HEADER = { 'x-admin-key': TEST_ADMIN_KEY };
|
|
21
|
+
const BASE_URL = `http://localhost:${PORT}`;
|
|
22
|
+
try {
|
|
23
|
+
// 1. Seed the test API key in the database
|
|
24
|
+
dbService = new db_service_js_1.FraimDbService();
|
|
25
|
+
await dbService.connect();
|
|
26
|
+
const db = dbService.db;
|
|
27
|
+
await db.collection('fraim_api_keys').updateOne({ key: TEST_API_KEY }, {
|
|
28
|
+
$set: {
|
|
29
|
+
userId: 'test-user@ashley.ai',
|
|
30
|
+
orgId: 'test-org',
|
|
31
|
+
isActive: true,
|
|
32
|
+
createdAt: new Date()
|
|
33
|
+
}
|
|
34
|
+
}, { upsert: true });
|
|
35
|
+
// 2. Start server in standalone mode
|
|
36
|
+
console.log(` Starting server on port ${PORT}...`);
|
|
37
|
+
const npxCommand = process.platform === 'win32' ? 'npx.cmd' : 'npx';
|
|
38
|
+
const serverScript = path_1.default.resolve(__dirname, '../src/fraim-mcp-server.ts');
|
|
39
|
+
fraimProcess = (0, node_child_process_1.spawn)(npxCommand, ['tsx', `"${serverScript}"`], {
|
|
40
|
+
env: {
|
|
41
|
+
...process.env,
|
|
42
|
+
FRAIM_MCP_PORT: PORT.toString(),
|
|
43
|
+
FRAIM_ADMIN_KEY: TEST_ADMIN_KEY,
|
|
44
|
+
FRAIM_SKIP_INDEX_ON_START: 'true' // Potential optimization if implemented
|
|
45
|
+
},
|
|
46
|
+
stdio: 'inherit',
|
|
47
|
+
shell: true
|
|
48
|
+
});
|
|
49
|
+
// 3. Wait for server to start
|
|
50
|
+
console.log(' Waiting for server to start...');
|
|
51
|
+
let started = false;
|
|
52
|
+
for (let i = 0; i < 15; i++) {
|
|
53
|
+
try {
|
|
54
|
+
await axios_1.default.get(`${BASE_URL}/health`, { timeout: 1000 });
|
|
55
|
+
started = true;
|
|
56
|
+
console.log(' Server started!');
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (!started) {
|
|
64
|
+
console.error(' ❌ Server failed to start within timeout');
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
// 4. Test health check (public)
|
|
68
|
+
console.log(' Testing public health check...');
|
|
69
|
+
const healthRes = await axios_1.default.get(`${BASE_URL}/health`, { timeout: 2000 });
|
|
70
|
+
node_assert_1.default.strictEqual(healthRes.status, 200);
|
|
71
|
+
node_assert_1.default.strictEqual(healthRes.data.status, 'ok');
|
|
72
|
+
// 5. Test MCP tool list without API key (fail)
|
|
73
|
+
console.log(' Testing MCP without auth (should fail)...');
|
|
74
|
+
try {
|
|
75
|
+
await axios_1.default.post(`${BASE_URL}/mcp`, {
|
|
76
|
+
jsonrpc: '2.0',
|
|
77
|
+
id: 1,
|
|
78
|
+
method: 'tools/list',
|
|
79
|
+
params: {}
|
|
80
|
+
}, { timeout: 2000 });
|
|
81
|
+
console.error(' ❌ Should have failed without API key');
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
node_assert_1.default.ok(error.response, 'Should have a response');
|
|
86
|
+
node_assert_1.default.strictEqual(error.response.status, 401);
|
|
87
|
+
}
|
|
88
|
+
// 6. Test MCP tool list with correct API key (success)
|
|
89
|
+
console.log(' Testing MCP with correct auth...');
|
|
90
|
+
const mcpResponse = await axios_1.default.post(`${BASE_URL}/mcp`, {
|
|
91
|
+
jsonrpc: '2.0',
|
|
92
|
+
id: 1,
|
|
93
|
+
method: 'tools/list',
|
|
94
|
+
params: {}
|
|
95
|
+
}, {
|
|
96
|
+
headers: { 'x-api-key': TEST_API_KEY },
|
|
97
|
+
timeout: 5000
|
|
98
|
+
});
|
|
99
|
+
node_assert_1.default.strictEqual(mcpResponse.status, 200);
|
|
100
|
+
node_assert_1.default.ok(mcpResponse.data.result.tools.length > 0);
|
|
101
|
+
// 7. Verify usage logging
|
|
102
|
+
console.log(' Verifying usage logging...');
|
|
103
|
+
await new Promise(resolve => setTimeout(resolve, 1500));
|
|
104
|
+
const log = await db.collection('fraim_usage_logs').findOne({ keyId: TEST_API_KEY });
|
|
105
|
+
node_assert_1.default.ok(log, 'Usage log should have been created');
|
|
106
|
+
node_assert_1.default.strictEqual(log.userId, 'test-user@ashley.ai');
|
|
107
|
+
// 8. Test Admin API - List Keys
|
|
108
|
+
console.log(' Testing Admin API - List Keys...');
|
|
109
|
+
const listKeysRes = await axios_1.default.get(`${BASE_URL}/admin/keys`, {
|
|
110
|
+
headers: TEST_ADMIN_HEADER,
|
|
111
|
+
timeout: 2000
|
|
112
|
+
});
|
|
113
|
+
node_assert_1.default.strictEqual(listKeysRes.status, 200);
|
|
114
|
+
node_assert_1.default.ok(Array.isArray(listKeysRes.data));
|
|
115
|
+
node_assert_1.default.ok(listKeysRes.data.some((k) => k.key === TEST_API_KEY));
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
console.error(' ❌ Test failed:', error);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
finally {
|
|
123
|
+
console.log(' Cleaning up...');
|
|
124
|
+
if (dbService) {
|
|
125
|
+
const db = dbService.db;
|
|
126
|
+
if (db) {
|
|
127
|
+
await db.collection('fraim_api_keys').deleteOne({ key: TEST_API_KEY }).catch(() => { });
|
|
128
|
+
}
|
|
129
|
+
await dbService.close().catch(() => { });
|
|
130
|
+
}
|
|
131
|
+
if (fraimProcess && fraimProcess.pid) {
|
|
132
|
+
const pid = fraimProcess.pid;
|
|
133
|
+
await new Promise((resolve) => (0, tree_kill_1.default)(pid, 'SIGKILL', () => resolve()));
|
|
134
|
+
console.log(` Terminated server process ${pid}`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
async function runFraimTest(testCase) {
|
|
139
|
+
return await testCase.testFunction();
|
|
140
|
+
}
|
|
141
|
+
const testCases = [
|
|
142
|
+
{
|
|
143
|
+
name: 'Fraim Standalone Server Integration',
|
|
144
|
+
description: 'Tests server startup, public health check, authenticated MCP access, usage logging, and admin API',
|
|
145
|
+
testFunction: testServerStartsAndResponds,
|
|
146
|
+
tags: ['smoke', 'fraim']
|
|
147
|
+
}
|
|
148
|
+
];
|
|
149
|
+
(0, test_utils_1.runTests)(testCases, runFraimTest, 'Fraim Standalone Server');
|