mirror of
https://github.com/numexai/numex-sdk.git
synced 2026-09-25 05:22:16 +00:00
Numex SDK: acik kaynak ilk surum (v1.1.0)
This commit is contained in:
parent
58b650ce4b
commit
0d263a3f94
17
.github/workflows/test.yml
vendored
Normal file
17
.github/workflows/test.yml
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
name: test
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
pull_request:
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
node: [18, 20, 22]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
- run: npm test
|
||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
node_modules/
|
||||
.env*
|
||||
*.log
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2025-2026 Numex AI Bilişim Teknolojileri
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
BIN
assets/numex-banner.png
Normal file
BIN
assets/numex-banner.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 204 KiB |
50
package.json
Normal file
50
package.json
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"name": "numexcodex-sdk",
|
||||
"version": "1.1.0",
|
||||
"description": "Official Node.js / TypeScript SDK for the Numex AI API and the Numex CLI — Türkçe yapay zekâ SDK'sı.",
|
||||
"main": "src/index.js",
|
||||
"types": "src/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"import": "./src/index.mjs",
|
||||
"require": "./src/index.js",
|
||||
"types": "./src/index.d.ts"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"files": [
|
||||
"src/",
|
||||
"package.json",
|
||||
"README.md",
|
||||
"LICENSE"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "node test/test.js && node test/cli-integration.test.js && node test/agentic-e2e.test.js"
|
||||
},
|
||||
"keywords": [
|
||||
"numex",
|
||||
"numex-ai",
|
||||
"ai",
|
||||
"llm",
|
||||
"sdk",
|
||||
"api",
|
||||
"cli",
|
||||
"agent",
|
||||
"turkish",
|
||||
"turkce",
|
||||
"yapay-zeka"
|
||||
],
|
||||
"author": "Numex AI Bilişim Teknolojileri <destek@numexai.com.tr>",
|
||||
"license": "MIT",
|
||||
"homepage": "https://github.com/mobilcep/numex-sdk#readme",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/mobilcep/numex-sdk.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/mobilcep/numex-sdk/issues"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
||||
147
src/bridge.js
Normal file
147
src/bridge.js
Normal file
|
|
@ -0,0 +1,147 @@
|
|||
const { spawn } = require('child_process');
|
||||
|
||||
/**
|
||||
* Clean helper to extract the last valid JSON object or array from stdout string
|
||||
*/
|
||||
function parseLastJson(stdout) {
|
||||
if (!stdout || typeof stdout !== 'string') return null;
|
||||
const clean = stdout.replace(/\x1B\[[0-9;]*[a-zA-Z]/g, '');
|
||||
const lines = clean.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
|
||||
|
||||
for (let i = lines.length - 1; i >= 0; i--) {
|
||||
const line = lines[i];
|
||||
if ((line.startsWith('{') && line.endsWith('}')) || (line.startsWith('[') && line.endsWith(']'))) {
|
||||
try {
|
||||
return JSON.parse(line);
|
||||
} catch (_) {}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Programmatic CLI bridge for Numex CLI execution.
|
||||
*/
|
||||
class NumexBridge {
|
||||
constructor(options = {}) {
|
||||
this._command = options.command || 'numex';
|
||||
this._getCwd = options.getCwd || (() => process.cwd());
|
||||
this._shell = options.shell === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes CLI command with arguments and returns structured result object.
|
||||
* @param {Array<string>} [args=[]] - Arguments to pass to CLI
|
||||
* @param {Object} [options={}] - Options for execution
|
||||
* @param {string} [options.cwd] - Custom working directory
|
||||
* @param {Object} [options.env] - Custom environment variables
|
||||
* @param {number} [options.timeout] - Timeout in milliseconds
|
||||
* @returns {Promise<{success: boolean, stdout: string, stderr: string, code: number|null, data?: any, error?: string}>}
|
||||
*/
|
||||
run(args = [], options = {}) {
|
||||
return new Promise((resolve) => {
|
||||
const command = options.command || this._command;
|
||||
const cwd = options.cwd || (this._getCwd ? this._getCwd() : process.cwd());
|
||||
const env = options.env ? { ...process.env, ...options.env } : process.env;
|
||||
const shell = options.shell !== undefined ? options.shell : this._shell;
|
||||
const timeout = typeof options.timeout === 'number' && options.timeout > 0 ? options.timeout : null;
|
||||
|
||||
let proc;
|
||||
let timer = null;
|
||||
let killedByTimeout = false;
|
||||
|
||||
try {
|
||||
proc = spawn(command, Array.isArray(args) ? args : [args], { cwd, env, shell });
|
||||
} catch (err) {
|
||||
return resolve({
|
||||
success: false,
|
||||
stdout: '',
|
||||
stderr: '',
|
||||
code: null,
|
||||
error: err.message || 'CLI süreci başlatılamadı.'
|
||||
});
|
||||
}
|
||||
|
||||
let stdout = '';
|
||||
let stderr = '';
|
||||
|
||||
if (timeout) {
|
||||
timer = setTimeout(() => {
|
||||
killedByTimeout = true;
|
||||
try {
|
||||
if (process.platform === 'win32') {
|
||||
spawn('taskkill', ['/pid', String(proc.pid), '/T', '/F'], { stdio: 'ignore' });
|
||||
} else {
|
||||
proc.kill('SIGKILL');
|
||||
}
|
||||
} catch (_) {
|
||||
try { proc.kill(); } catch (_) {}
|
||||
}
|
||||
}, timeout);
|
||||
}
|
||||
|
||||
if (proc.stdout) {
|
||||
proc.stdout.on('data', (d) => { stdout += d.toString(); });
|
||||
}
|
||||
if (proc.stderr) {
|
||||
proc.stderr.on('data', (d) => { stderr += d.toString(); });
|
||||
}
|
||||
|
||||
proc.on('error', (err) => {
|
||||
if (timer) clearTimeout(timer);
|
||||
resolve({
|
||||
success: false,
|
||||
stdout,
|
||||
stderr,
|
||||
code: null,
|
||||
error: err.message || 'CLI yürütme hatası'
|
||||
});
|
||||
});
|
||||
|
||||
proc.on('close', (code) => {
|
||||
if (timer) clearTimeout(timer);
|
||||
|
||||
if (killedByTimeout) {
|
||||
return resolve({
|
||||
success: false,
|
||||
stdout,
|
||||
stderr,
|
||||
code: null,
|
||||
error: `CLI komutu zaman aşımına uğradı (${timeout}ms)`
|
||||
});
|
||||
}
|
||||
|
||||
if (code !== 0 && code !== null) {
|
||||
return resolve({
|
||||
success: false,
|
||||
stdout,
|
||||
stderr,
|
||||
code,
|
||||
error: stderr.trim() || `CLI komutu ${code} çıkış kodu ile sonlandı.`
|
||||
});
|
||||
}
|
||||
|
||||
const parsedData = parseLastJson(stdout);
|
||||
const data = parsedData !== null ? parsedData : stdout.trim();
|
||||
|
||||
resolve({
|
||||
success: true,
|
||||
stdout,
|
||||
stderr,
|
||||
code: 0,
|
||||
data
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for simple JSON output retrieval
|
||||
*/
|
||||
async runJson(args = [], options = {}) {
|
||||
const res = await this.run(args, options);
|
||||
return res.success ? res.data : null;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { NumexBridge, parseLastJson };
|
||||
107
src/index.d.ts
vendored
Normal file
107
src/index.d.ts
vendored
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
export interface NumexOptions {
|
||||
apiKey?: string;
|
||||
baseURL?: string;
|
||||
/** Node executable path (e.g. process.execPath) or CLI command name (e.g. 'numex') */
|
||||
command?: string;
|
||||
cwd?: string;
|
||||
requireApiKey?: boolean;
|
||||
}
|
||||
|
||||
export interface NumexBridgeOptions {
|
||||
/** Node executable path (e.g. process.execPath) or CLI command name (e.g. 'numex') */
|
||||
command?: string;
|
||||
getCwd?: () => string;
|
||||
shell?: boolean;
|
||||
}
|
||||
|
||||
export interface NumexBridgeRunOptions {
|
||||
cwd?: string;
|
||||
env?: Record<string, string>;
|
||||
timeout?: number;
|
||||
command?: string;
|
||||
}
|
||||
|
||||
export interface NumexBridgeResult {
|
||||
success: boolean;
|
||||
stdout: string;
|
||||
stderr: string;
|
||||
code: number | null;
|
||||
data?: any;
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export class NumexBridge {
|
||||
constructor(options?: NumexBridgeOptions);
|
||||
run(args?: string[], options?: NumexBridgeRunOptions): Promise<NumexBridgeResult>;
|
||||
runJson(args?: string[], options?: NumexBridgeRunOptions): Promise<any | null>;
|
||||
}
|
||||
|
||||
export interface ChatMessage {
|
||||
role: 'system' | 'user' | 'assistant';
|
||||
content: string | any[];
|
||||
}
|
||||
|
||||
export interface ChatCompletionRequest {
|
||||
messages: ChatMessage[];
|
||||
model?: string;
|
||||
stream?: boolean;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export class ChatCompletions {
|
||||
create(params: ChatCompletionRequest): Promise<any>;
|
||||
}
|
||||
|
||||
export class Chat {
|
||||
completions: ChatCompletions;
|
||||
}
|
||||
|
||||
export class Images {
|
||||
generate(params: {
|
||||
prompt: string;
|
||||
n?: number;
|
||||
size?: string;
|
||||
[key: string]: any;
|
||||
}): Promise<any>;
|
||||
}
|
||||
|
||||
export class Embeddings {
|
||||
create(params: {
|
||||
input: string | string[];
|
||||
model?: string;
|
||||
[key: string]: any;
|
||||
}): Promise<any>;
|
||||
}
|
||||
|
||||
export class Models {
|
||||
list(): Promise<any>;
|
||||
}
|
||||
|
||||
export class Search {
|
||||
query(params: {
|
||||
query: string;
|
||||
[key: string]: any;
|
||||
}): Promise<any>;
|
||||
}
|
||||
|
||||
export class NumexApiClient {
|
||||
constructor(baseURL: string, apiKey?: string);
|
||||
post(path: string, data?: any, options?: any): Promise<any>;
|
||||
get(path: string, options?: any): Promise<any>;
|
||||
}
|
||||
|
||||
export class Numex {
|
||||
constructor(options?: NumexOptions);
|
||||
apiKey?: string;
|
||||
baseURL: string;
|
||||
client: NumexApiClient;
|
||||
cli: NumexBridge;
|
||||
chat: Chat;
|
||||
images: Images;
|
||||
embeddings: Embeddings;
|
||||
models: Models;
|
||||
search: Search;
|
||||
run(command: string | string[], args?: string[] | Record<string, any>, options?: NumexBridgeRunOptions): Promise<NumexBridgeResult>;
|
||||
}
|
||||
|
||||
export default Numex;
|
||||
119
src/index.js
Normal file
119
src/index.js
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
const Chat = require('./resources/chat');
|
||||
const Images = require('./resources/images');
|
||||
const Embeddings = require('./resources/embeddings');
|
||||
const Models = require('./resources/models');
|
||||
const Search = require('./resources/search');
|
||||
const { NumexBridge } = require('./bridge');
|
||||
|
||||
class NumexApiClient {
|
||||
constructor(baseURL, apiKey) {
|
||||
this.baseURL = baseURL;
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
async _request(path, options = {}) {
|
||||
const url = `${this.baseURL}${path}`;
|
||||
const headers = {
|
||||
'Authorization': `Bearer ${this.apiKey}`,
|
||||
'User-Agent': 'numex-node-sdk/1.1.0',
|
||||
...(options.headers || {})
|
||||
};
|
||||
|
||||
if (options.body && typeof options.body === 'object') {
|
||||
options.body = JSON.stringify(options.body);
|
||||
headers['Content-Type'] = 'application/json';
|
||||
}
|
||||
|
||||
const res = await fetch(url, { ...options, headers });
|
||||
|
||||
// Handle stream response
|
||||
if (options.stream) {
|
||||
if (!res.ok) {
|
||||
const errText = await res.text();
|
||||
throw new Error(`Numex API Error [${res.status}]: ${errText}`);
|
||||
}
|
||||
return res.body; // Return the ReadableStream
|
||||
}
|
||||
|
||||
// Handle normal JSON response
|
||||
const data = await res.json().catch(() => ({}));
|
||||
|
||||
if (!res.ok) {
|
||||
throw new Error(`Numex API Error [${res.status}]: ${data.error ? JSON.stringify(data.error) : JSON.stringify(data)}`);
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
post(path, data, options = {}) {
|
||||
return this._request(path, { method: 'POST', body: data, ...options });
|
||||
}
|
||||
|
||||
get(path, options = {}) {
|
||||
return this._request(path, { method: 'GET', ...options });
|
||||
}
|
||||
}
|
||||
|
||||
class Numex {
|
||||
/**
|
||||
* Initialize the Numex API Client & CLI Bridge
|
||||
* @param {Object} options
|
||||
* @param {string} [options.apiKey] - Your Numex Developer API Key
|
||||
* @param {string} [options.baseURL] - Optional custom base URL
|
||||
* @param {string} [options.command] - Optional custom CLI command name
|
||||
* @param {string} [options.cwd] - Optional working directory
|
||||
*/
|
||||
constructor(options = {}) {
|
||||
this.apiKey = options.apiKey || process.env.NUMEX_API_KEY;
|
||||
if (!this.apiKey && options.requireApiKey !== false) {
|
||||
throw new Error("Numex API Key is required. Pass it via options or set NUMEX_API_KEY environment variable.");
|
||||
}
|
||||
|
||||
this.baseURL = options.baseURL || 'https://api.numexai.com.tr/v1';
|
||||
this.client = new NumexApiClient(this.baseURL, this.apiKey);
|
||||
|
||||
// CLI Bridge for programmatic CLI execution
|
||||
this.cli = new NumexBridge({
|
||||
command: options.command || 'numex',
|
||||
getCwd: () => options.cwd || process.cwd()
|
||||
});
|
||||
|
||||
// Sub-modules
|
||||
this.chat = new Chat(this.client);
|
||||
this.images = new Images(this.client);
|
||||
this.embeddings = new Embeddings(this.client);
|
||||
this.models = new Models(this.client);
|
||||
this.search = new Search(this.client);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run programmatic CLI command
|
||||
* @param {string|Array<string>} command - CLI subcommand or array of arguments
|
||||
* @param {Array<string>|Object} [args=[]] - Additional arguments or options if args omitted
|
||||
* @param {Object} [options={}] - Options like timeout, cwd, env
|
||||
* @example await numex.run('coklu_duzenle', ['src/'], { timeout: 60000 })
|
||||
*/
|
||||
async run(command, args = [], options = {}) {
|
||||
let fullArgs = [];
|
||||
|
||||
if (Array.isArray(command)) {
|
||||
fullArgs = [...command];
|
||||
if (typeof args === 'object' && !Array.isArray(args)) {
|
||||
options = args;
|
||||
} else if (Array.isArray(args)) {
|
||||
fullArgs.push(...args);
|
||||
}
|
||||
} else if (typeof command === 'string') {
|
||||
fullArgs = [command];
|
||||
if (typeof args === 'object' && !Array.isArray(args)) {
|
||||
options = args;
|
||||
} else if (Array.isArray(args)) {
|
||||
fullArgs.push(...args);
|
||||
}
|
||||
}
|
||||
|
||||
return this.cli.run(fullArgs, options);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { Numex, NumexApiClient, NumexBridge };
|
||||
7
src/index.mjs
Normal file
7
src/index.mjs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { createRequire } from 'node:module';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
const { Numex, NumexBridge, NumexApiClient } = require('./index.js');
|
||||
|
||||
export { Numex, NumexBridge, NumexApiClient };
|
||||
export default Numex;
|
||||
21
src/resources/chat.js
Normal file
21
src/resources/chat.js
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
class ChatCompletions {
|
||||
constructor(client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
async create(params) {
|
||||
// If stream is requested, use the appropriate streaming endpoint/method
|
||||
if (params.stream) {
|
||||
return this.client.post('/chat/stream', params, { stream: true });
|
||||
}
|
||||
return this.client.post('/chat', params);
|
||||
}
|
||||
}
|
||||
|
||||
class Chat {
|
||||
constructor(client) {
|
||||
this.completions = new ChatCompletions(client);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Chat;
|
||||
11
src/resources/embeddings.js
Normal file
11
src/resources/embeddings.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
class Embeddings {
|
||||
constructor(client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
async create(params) {
|
||||
return this.client.post('/embeddings', params);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Embeddings;
|
||||
11
src/resources/images.js
Normal file
11
src/resources/images.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
class Images {
|
||||
constructor(client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
async generate(params) {
|
||||
return this.client.post('/images/generations', params);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Images;
|
||||
11
src/resources/models.js
Normal file
11
src/resources/models.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
class Models {
|
||||
constructor(client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
async list() {
|
||||
return this.client.get('/models');
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Models;
|
||||
11
src/resources/search.js
Normal file
11
src/resources/search.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
class Search {
|
||||
constructor(client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
async query(params) {
|
||||
return this.client.post('/search', params);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Search;
|
||||
51
test/agentic-e2e.test.js
Normal file
51
test/agentic-e2e.test.js
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { Numex } = require('../src/index');
|
||||
|
||||
async function testAgenticE2E() {
|
||||
console.log('🧪 Numex SDK → CLI → @numex/core Agentic Zincir Testi...\n');
|
||||
|
||||
const cliPath = path.resolve(__dirname, '../../numex-cli/bin/numex.js');
|
||||
const projectRoot = path.resolve(__dirname, '../..');
|
||||
|
||||
if (!fs.existsSync(cliPath)) {
|
||||
console.log('⚠️ SKIP: numex CLI bulunamadı (' + cliPath + ')');
|
||||
return;
|
||||
}
|
||||
|
||||
const numex = new Numex({
|
||||
requireApiKey: false,
|
||||
command: process.execPath
|
||||
});
|
||||
|
||||
// 1) numex.run() ile 'guard' (Commit Risk Taraması - @numex/core/commitGuard.js)
|
||||
console.log('1) SDK üzerinden numex.run("guard", ["--json"]) tetikleniyor...');
|
||||
const guardRes = await numex.run([cliPath, 'guard', '--json'], { cwd: projectRoot, timeout: 15000 });
|
||||
|
||||
console.log(' Çıkış Kodu:', guardRes.code);
|
||||
console.log(' Success:', guardRes.success);
|
||||
console.log(' Ayrıştırılan Data:\n', JSON.stringify(guardRes.data, null, 2));
|
||||
|
||||
assert.strictEqual(guardRes.success, true, 'Guard komutu başarılı olmalı');
|
||||
assert.ok(guardRes.data && typeof guardRes.data === 'object', 'Guard çıktısı JSON nesnesi olarak parse edilmeli');
|
||||
assert.ok('risk' in guardRes.data || 'ok' in guardRes.data, 'Guard verisi risk veya ok alanını içermeli');
|
||||
|
||||
// 2) numex.run() ile 'harita' (Proje Haritası Üretici - @numex/core/projectMapGen.js)
|
||||
console.log('\n2) SDK üzerinden numex.run("harita", ["--dry"]) tetikleniyor...');
|
||||
const mapRes = await numex.run([cliPath, 'harita', '--dry'], { cwd: projectRoot, timeout: 15000 });
|
||||
|
||||
console.log(' Çıkış Kodu:', mapRes.code);
|
||||
console.log(' Success:', mapRes.success);
|
||||
console.log(' Stdout İlk 250 Karakter:\n', mapRes.stdout.slice(0, 250));
|
||||
|
||||
assert.strictEqual(mapRes.success, true, 'Harita komutu başarılı olmalı');
|
||||
assert.ok(mapRes.stdout.includes('PROJECT_MAP') || mapRes.stdout.includes('dry') || mapRes.stdout.includes('Numex'), 'Harita çıktısı üretilmeli');
|
||||
|
||||
console.log('\n🎉 Zincir Testi Başarılı: SDK (numex.run) → CLI (bin/numex.js) → @numex/core zinciri somut olarak doğrulandı!');
|
||||
}
|
||||
|
||||
testAgenticE2E().catch((err) => {
|
||||
console.error('❌ Agentic E2E testi başarısız:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
43
test/cli-integration.test.js
Normal file
43
test/cli-integration.test.js
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
const assert = require('assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { NumexBridge } = require('../src/index');
|
||||
|
||||
async function testCliIntegration() {
|
||||
console.log('🧪 Gerçek Numex CLI Entegrasyon Testi Başlatılıyor...\n');
|
||||
|
||||
// CLI yolunu belirle: c:/numexai/numexai/numex-cli/bin/numex.js
|
||||
const cliPath = path.resolve(__dirname, '../../numex-cli/bin/numex.js');
|
||||
|
||||
if (!fs.existsSync(cliPath)) {
|
||||
console.log('⚠️ SKIP: numex CLI bulunamadı (' + cliPath + ')');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('🔍 Bulunan CLI Giriş Noktası:', cliPath);
|
||||
|
||||
// Node.js ile bin/numex.js çalıştırmak için command = process.execPath ve args = [cliPath, '--help']
|
||||
const bridge = new NumexBridge({
|
||||
command: process.execPath
|
||||
});
|
||||
|
||||
const res = await bridge.run([cliPath, '--help'], { timeout: 15000 });
|
||||
|
||||
if (!res.success && res.error && (res.error.includes('ENOENT') || res.error.includes('tanınmıyor'))) {
|
||||
console.log('⚠️ SKIP: numex CLI bulunamadı veya çalıştırılamadı.');
|
||||
return;
|
||||
}
|
||||
|
||||
assert.strictEqual(res.success, true, `CLI çağrısı başarılı olmalı (Hata: ${res.error})`);
|
||||
assert.ok(
|
||||
res.stdout.includes('Kullanım:') || res.stdout.includes('numex') || res.stdout.includes('Numex'),
|
||||
'stdout içerisinde yardım veya Numex metni geçmeli'
|
||||
);
|
||||
|
||||
console.log('✅ Gerçek Numex CLI entegrasyon testi başarıyla geçti!');
|
||||
}
|
||||
|
||||
testCliIntegration().catch((err) => {
|
||||
console.error('❌ CLI Entegrasyon testi başarısız:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
61
test/test.js
Normal file
61
test/test.js
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
const assert = require('assert');
|
||||
const { Numex, NumexBridge } = require('../src/index');
|
||||
|
||||
async function runTests() {
|
||||
console.log('🧪 Numex SDK & CLI Bridge Testleri Başlatılıyor...\n');
|
||||
|
||||
// Test 1: Numex Initialization & Sub-modules
|
||||
const numex = new Numex({ apiKey: 'test_key_123' });
|
||||
|
||||
assert.ok(numex.chat, 'Chat modülü mevcut olmalı');
|
||||
assert.ok(numex.images, 'Images modülü mevcut olmalı');
|
||||
assert.ok(numex.embeddings, 'Embeddings modülü mevcut olmalı');
|
||||
assert.ok(numex.models, 'Models modülü mevcut olmalı');
|
||||
assert.ok(numex.search, 'Search modülü mevcut olmalı');
|
||||
assert.ok(numex.cli, 'CLI bridge nesnesi mevcut olmalı');
|
||||
assert.strictEqual(typeof numex.run, 'function', 'numex.run metodu fonksiyon olmalı');
|
||||
|
||||
console.log('✅ Test 1: Numex SDK başarıyla başlatıldı ve modüller doğrulandı.');
|
||||
|
||||
// Test 2: Numex.run metodu ve komut yapılandırma testi
|
||||
assert.strictEqual(typeof numex.run, 'function', 'numex.run metodu mevcut olmalı');
|
||||
|
||||
// mock CLI bridge kullanarak run metodunu test edelim
|
||||
const mockBridge = new NumexBridge({ command: process.execPath });
|
||||
const nodeVersionResult = await mockBridge.run(['-v']);
|
||||
assert.strictEqual(nodeVersionResult.success, true, 'Node -v komutu başarılı olmalı');
|
||||
assert.ok(nodeVersionResult.stdout.includes('v'), 'stdout Node sürümünü içermeli');
|
||||
|
||||
console.log('✅ Test 2: numex.run metodu ve NumexBridge programatik çalıştırma doğrulandı.');
|
||||
|
||||
// Test 3: NumexBridge doğrudan nesne testi ve JSON parse testi
|
||||
const customBridge = new NumexBridge({ command: process.execPath });
|
||||
const jsonTestResult = await customBridge.run([
|
||||
'-e',
|
||||
'console.log("some log"); console.log(JSON.stringify({ status: "ok", count: 42 }));'
|
||||
]);
|
||||
|
||||
assert.strictEqual(jsonTestResult.success, true, 'Custom bridge çalıştırma başarılı olmalı');
|
||||
assert.deepStrictEqual(jsonTestResult.data, { status: 'ok', count: 42 }, 'Son JSON çıktısı başarıyla parse edilmeli');
|
||||
|
||||
console.log('✅ Test 3: NumexBridge doğrudan testi ve otomatik JSON ayrıştırma doğrulandı.');
|
||||
|
||||
// Test 4: Hata ve Zaman Aşımı Yönetimi (Error & Timeout Handling)
|
||||
const timeoutBridge = new NumexBridge({ command: process.execPath });
|
||||
const timeoutResult = await timeoutBridge.run(
|
||||
['-e', 'setTimeout(() => {}, 10000);'],
|
||||
{ timeout: 300 }
|
||||
);
|
||||
|
||||
assert.strictEqual(timeoutResult.success, false, 'Zaman aşımı sonrası success: false dönmeli');
|
||||
assert.ok(timeoutResult.error.includes('zaman aşımı'), 'Hata mesajı zaman aşımını belirtmeli');
|
||||
|
||||
console.log('✅ Test 4: Güvenli hata ve zaman aşımı (timeout) yönetimi doğrulandı.');
|
||||
|
||||
console.log('\n🎉 TÜM TESTLER BAŞARIYLA TAMAMLANDI!');
|
||||
}
|
||||
|
||||
runTests().catch((err) => {
|
||||
console.error('❌ Test başarısız:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
Loading…
Reference in New Issue
Block a user