@team-semicolon/semo-cli 4.0.0 → 4.0.2
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/dist/commands/bots.js +34 -0
- package/dist/database.js +7 -5
- package/dist/index.js +4 -4
- package/dist/kb.d.ts +2 -2
- package/dist/kb.js +11 -11
- package/package.json +1 -1
package/dist/commands/bots.js
CHANGED
|
@@ -345,4 +345,38 @@ function registerBotsCommands(program) {
|
|
|
345
345
|
await (0, database_1.closeConnection)();
|
|
346
346
|
}
|
|
347
347
|
});
|
|
348
|
+
// ── semo bots set-status ─────────────────────────────────────
|
|
349
|
+
botsCmd
|
|
350
|
+
.command("set-status <bot_id> <status>")
|
|
351
|
+
.description("봇 온라인 상태 수동 설정 (online|offline)")
|
|
352
|
+
.action(async (botId, status) => {
|
|
353
|
+
if (status !== "online" && status !== "offline") {
|
|
354
|
+
console.log(chalk_1.default.red("❌ status는 'online' 또는 'offline'만 가능합니다."));
|
|
355
|
+
process.exit(1);
|
|
356
|
+
}
|
|
357
|
+
const connected = await (0, database_1.isDbConnected)();
|
|
358
|
+
if (!connected) {
|
|
359
|
+
console.log(chalk_1.default.red("❌ DB 연결 실패"));
|
|
360
|
+
await (0, database_1.closeConnection)();
|
|
361
|
+
process.exit(1);
|
|
362
|
+
}
|
|
363
|
+
try {
|
|
364
|
+
const pool = (0, database_1.getPool)();
|
|
365
|
+
const client = await pool.connect();
|
|
366
|
+
await client.query(`INSERT INTO semo.bot_status (bot_id, status, synced_at)
|
|
367
|
+
VALUES ($1, $2, NOW())
|
|
368
|
+
ON CONFLICT (bot_id) DO UPDATE SET
|
|
369
|
+
status = EXCLUDED.status,
|
|
370
|
+
synced_at = NOW()`, [botId, status]);
|
|
371
|
+
client.release();
|
|
372
|
+
console.log(chalk_1.default.green(`✔ ${botId} → ${status}`));
|
|
373
|
+
}
|
|
374
|
+
catch (err) {
|
|
375
|
+
console.log(chalk_1.default.red(`❌ 실패: ${err}`));
|
|
376
|
+
process.exit(1);
|
|
377
|
+
}
|
|
378
|
+
finally {
|
|
379
|
+
await (0, database_1.closeConnection)();
|
|
380
|
+
}
|
|
381
|
+
});
|
|
348
382
|
}
|
package/dist/database.js
CHANGED
|
@@ -31,23 +31,25 @@ function buildDbConfig() {
|
|
|
31
31
|
idleTimeoutMillis: 30000,
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
|
+
if (!process.env.SEMO_DB_HOST && !process.env.DATABASE_URL) {
|
|
35
|
+
throw new Error("DB 연결 정보가 없습니다. DATABASE_URL 또는 SEMO_DB_HOST 환경변수를 설정하세요.");
|
|
36
|
+
}
|
|
34
37
|
return {
|
|
35
|
-
host: process.env.SEMO_DB_HOST
|
|
38
|
+
host: process.env.SEMO_DB_HOST,
|
|
36
39
|
port: parseInt(process.env.SEMO_DB_PORT || "5432"),
|
|
37
40
|
user: process.env.SEMO_DB_USER || "app",
|
|
38
|
-
password: process.env.SEMO_DB_PASSWORD
|
|
41
|
+
password: process.env.SEMO_DB_PASSWORD,
|
|
39
42
|
database: process.env.SEMO_DB_NAME || "appdb",
|
|
40
43
|
ssl: false,
|
|
41
44
|
connectionTimeoutMillis: 5000,
|
|
42
45
|
idleTimeoutMillis: 30000,
|
|
43
46
|
};
|
|
44
47
|
}
|
|
45
|
-
|
|
46
|
-
// PostgreSQL Pool (싱글톤)
|
|
48
|
+
// PostgreSQL Pool (싱글톤) — 최초 getPool() 호출 시점에 config 평가
|
|
47
49
|
let pool = null;
|
|
48
50
|
function getPool() {
|
|
49
51
|
if (!pool) {
|
|
50
|
-
pool = new pg_1.Pool(
|
|
52
|
+
pool = new pg_1.Pool(buildDbConfig());
|
|
51
53
|
}
|
|
52
54
|
return pool;
|
|
53
55
|
}
|
package/dist/index.js
CHANGED
|
@@ -2644,14 +2644,14 @@ kbCmd
|
|
|
2644
2644
|
});
|
|
2645
2645
|
kbCmd
|
|
2646
2646
|
.command("embed")
|
|
2647
|
-
.description("기존 KB 항목에 임베딩 벡터 생성 (
|
|
2647
|
+
.description("기존 KB 항목에 임베딩 벡터 생성 (OPENAI_API_KEY 필요)")
|
|
2648
2648
|
.option("--bot <name>", "봇 KB도 임베딩", detectBotId())
|
|
2649
2649
|
.option("--domain <name>", "도메인 필터")
|
|
2650
2650
|
.option("--force", "이미 임베딩된 항목도 재생성")
|
|
2651
2651
|
.action(async (options) => {
|
|
2652
|
-
if (!process.env.
|
|
2653
|
-
console.log(chalk_1.default.red("❌
|
|
2654
|
-
console.log(chalk_1.default.gray(" export
|
|
2652
|
+
if (!process.env.OPENAI_API_KEY) {
|
|
2653
|
+
console.log(chalk_1.default.red("❌ OPENAI_API_KEY 환경변수가 설정되지 않았습니다."));
|
|
2654
|
+
console.log(chalk_1.default.gray(" export OPENAI_API_KEY='sk-...'"));
|
|
2655
2655
|
process.exit(1);
|
|
2656
2656
|
}
|
|
2657
2657
|
const spinner = (0, ora_1.default)("임베딩 대상 조회 중...").start();
|
package/dist/kb.d.ts
CHANGED
|
@@ -9,12 +9,12 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { Pool } from "pg";
|
|
11
11
|
/**
|
|
12
|
-
* Generate embedding vector for text using OpenAI API
|
|
12
|
+
* Generate embedding vector for text using OpenAI Embeddings API
|
|
13
13
|
* Requires OPENAI_API_KEY environment variable
|
|
14
14
|
*/
|
|
15
15
|
export declare function generateEmbedding(text: string): Promise<number[] | null>;
|
|
16
16
|
/**
|
|
17
|
-
* Generate embeddings for multiple texts (
|
|
17
|
+
* Generate embeddings for multiple texts (OpenAI는 단건 처리, 순차 호출)
|
|
18
18
|
*/
|
|
19
19
|
export declare function generateEmbeddings(texts: string[]): Promise<(number[] | null)[]>;
|
|
20
20
|
export interface KBEntry {
|
package/dist/kb.js
CHANGED
|
@@ -59,18 +59,18 @@ const path = __importStar(require("path"));
|
|
|
59
59
|
// ============================================================
|
|
60
60
|
// Embedding
|
|
61
61
|
// ============================================================
|
|
62
|
-
const EMBEDDING_MODEL = "
|
|
63
|
-
const EMBEDDING_DIMENSIONS = 1024;
|
|
62
|
+
const EMBEDDING_MODEL = "text-embedding-3-small";
|
|
63
|
+
const EMBEDDING_DIMENSIONS = 1024; // DB vector(1024) 유지 — OpenAI dimensions 파라미터로 축소
|
|
64
64
|
/**
|
|
65
|
-
* Generate embedding vector for text using OpenAI API
|
|
65
|
+
* Generate embedding vector for text using OpenAI Embeddings API
|
|
66
66
|
* Requires OPENAI_API_KEY environment variable
|
|
67
67
|
*/
|
|
68
68
|
async function generateEmbedding(text) {
|
|
69
|
-
const apiKey = process.env.
|
|
69
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
70
70
|
if (!apiKey)
|
|
71
71
|
return null;
|
|
72
72
|
try {
|
|
73
|
-
const response = await fetch("https://api.
|
|
73
|
+
const response = await fetch("https://api.openai.com/v1/embeddings", {
|
|
74
74
|
method: "POST",
|
|
75
75
|
headers: {
|
|
76
76
|
"Authorization": `Bearer ${apiKey}`,
|
|
@@ -78,7 +78,7 @@ async function generateEmbedding(text) {
|
|
|
78
78
|
},
|
|
79
79
|
body: JSON.stringify({
|
|
80
80
|
model: EMBEDDING_MODEL,
|
|
81
|
-
input: text.substring(0, 8000),
|
|
81
|
+
input: text.substring(0, 8000),
|
|
82
82
|
dimensions: EMBEDDING_DIMENSIONS,
|
|
83
83
|
}),
|
|
84
84
|
});
|
|
@@ -96,14 +96,14 @@ async function generateEmbedding(text) {
|
|
|
96
96
|
}
|
|
97
97
|
}
|
|
98
98
|
/**
|
|
99
|
-
* Generate embeddings for multiple texts (
|
|
99
|
+
* Generate embeddings for multiple texts (OpenAI는 단건 처리, 순차 호출)
|
|
100
100
|
*/
|
|
101
101
|
async function generateEmbeddings(texts) {
|
|
102
|
-
const apiKey = process.env.
|
|
102
|
+
const apiKey = process.env.OPENAI_API_KEY;
|
|
103
103
|
if (!apiKey)
|
|
104
104
|
return texts.map(() => null);
|
|
105
105
|
try {
|
|
106
|
-
const response = await fetch("https://api.
|
|
106
|
+
const response = await fetch("https://api.openai.com/v1/embeddings", {
|
|
107
107
|
method: "POST",
|
|
108
108
|
headers: {
|
|
109
109
|
"Authorization": `Bearer ${apiKey}`,
|
|
@@ -111,8 +111,8 @@ async function generateEmbeddings(texts) {
|
|
|
111
111
|
},
|
|
112
112
|
body: JSON.stringify({
|
|
113
113
|
model: EMBEDDING_MODEL,
|
|
114
|
-
input: texts.map(t => t.substring(0,
|
|
115
|
-
|
|
114
|
+
input: texts.map(t => t.substring(0, 8000)),
|
|
115
|
+
dimensions: EMBEDDING_DIMENSIONS,
|
|
116
116
|
}),
|
|
117
117
|
});
|
|
118
118
|
if (!response.ok)
|