@team-semicolon/semo-cli 4.7.2 → 4.7.4
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/index.js +104 -6
- package/dist/kb.d.ts +25 -0
- package/dist/kb.js +74 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1508,6 +1508,7 @@ kbCmd
|
|
|
1508
1508
|
.option("--service <name>", "서비스(프로젝트) 필터")
|
|
1509
1509
|
.option("--limit <n>", "최대 결과 수", "10")
|
|
1510
1510
|
.option("--mode <type>", "검색 모드 (hybrid|semantic|text)", "hybrid")
|
|
1511
|
+
.option("--short", "미리보기 모드 (content를 80자로 잘라서 표시)")
|
|
1511
1512
|
.action(async (query, options) => {
|
|
1512
1513
|
const spinner = (0, ora_1.default)(`'${query}' 검색 중...`).start();
|
|
1513
1514
|
try {
|
|
@@ -1525,12 +1526,17 @@ kbCmd
|
|
|
1525
1526
|
else {
|
|
1526
1527
|
console.log(chalk_1.default.cyan.bold(`\n🔍 검색 결과: '${query}' (${results.length}건)\n`));
|
|
1527
1528
|
for (const entry of results) {
|
|
1528
|
-
const preview = entry.content.substring(0, 80).replace(/\n/g, " ");
|
|
1529
1529
|
const score = entry.score;
|
|
1530
1530
|
const scoreStr = score ? chalk_1.default.yellow(` (${(score * 100).toFixed(1)}%)`) : "";
|
|
1531
1531
|
const fullKey = entry.sub_key ? `${entry.key}/${entry.sub_key}` : entry.key;
|
|
1532
1532
|
console.log(chalk_1.default.cyan(` [${entry.domain}] `) + chalk_1.default.white(fullKey) + scoreStr);
|
|
1533
|
-
|
|
1533
|
+
if (options.short) {
|
|
1534
|
+
const preview = entry.content.substring(0, 80).replace(/\n/g, " ");
|
|
1535
|
+
console.log(chalk_1.default.gray(` ${preview}${entry.content.length > 80 ? "..." : ""}`));
|
|
1536
|
+
}
|
|
1537
|
+
else {
|
|
1538
|
+
console.log(chalk_1.default.gray(` ${entry.content}`));
|
|
1539
|
+
}
|
|
1534
1540
|
console.log();
|
|
1535
1541
|
}
|
|
1536
1542
|
}
|
|
@@ -1699,9 +1705,13 @@ kbCmd
|
|
|
1699
1705
|
kbCmd
|
|
1700
1706
|
.command("ontology")
|
|
1701
1707
|
.description("온톨로지 조회 — 도메인/타입/스키마/라우팅 테이블")
|
|
1702
|
-
.option("--action <type>", "
|
|
1703
|
-
.option("--domain <name>", "action=show 시 도메인")
|
|
1704
|
-
.option("--type <name>", "action=schema 시 타입 키")
|
|
1708
|
+
.option("--action <type>", "동작 (list|show|services|types|instances|schema|routing-table|register)", "list")
|
|
1709
|
+
.option("--domain <name>", "action=show|register 시 도메인")
|
|
1710
|
+
.option("--type <name>", "action=schema|register 시 타입 키")
|
|
1711
|
+
.option("--description <text>", "action=register 시 설명")
|
|
1712
|
+
.option("--service <name>", "action=register 시 서비스 그룹")
|
|
1713
|
+
.option("--tags <tags>", "action=register 시 태그 (쉼표 구분)")
|
|
1714
|
+
.option("--no-init", "action=register 시 필수 KB entry 자동 생성 건너뛰기")
|
|
1705
1715
|
.option("--format <type>", "출력 형식 (json|table)", "table")
|
|
1706
1716
|
.action(async (options) => {
|
|
1707
1717
|
try {
|
|
@@ -1846,8 +1856,48 @@ kbCmd
|
|
|
1846
1856
|
console.log();
|
|
1847
1857
|
}
|
|
1848
1858
|
}
|
|
1859
|
+
else if (action === "register") {
|
|
1860
|
+
if (!options.domain) {
|
|
1861
|
+
console.log(chalk_1.default.red("--domain 옵션이 필요합니다."));
|
|
1862
|
+
process.exit(1);
|
|
1863
|
+
}
|
|
1864
|
+
if (!options.type) {
|
|
1865
|
+
console.log(chalk_1.default.red("--type 옵션이 필요합니다. (예: --type service, --type team, --type person)"));
|
|
1866
|
+
const types = await (0, kb_1.ontoListTypes)(pool);
|
|
1867
|
+
console.log(chalk_1.default.gray(`사용 가능한 타입: ${types.map(t => t.type_key).join(", ")}`));
|
|
1868
|
+
process.exit(1);
|
|
1869
|
+
}
|
|
1870
|
+
const tags = options.tags ? options.tags.split(",").map((t) => t.trim()) : undefined;
|
|
1871
|
+
const result = await (0, kb_1.ontoRegister)(pool, {
|
|
1872
|
+
domain: options.domain,
|
|
1873
|
+
entity_type: options.type,
|
|
1874
|
+
description: options.description,
|
|
1875
|
+
service: options.service,
|
|
1876
|
+
tags,
|
|
1877
|
+
init_required: options.init !== false,
|
|
1878
|
+
});
|
|
1879
|
+
if (options.format === "json") {
|
|
1880
|
+
console.log(JSON.stringify(result, null, 2));
|
|
1881
|
+
}
|
|
1882
|
+
else {
|
|
1883
|
+
if (result.success) {
|
|
1884
|
+
console.log(chalk_1.default.green(`\n✅ 도메인 '${options.domain}' 등록 완료 (타입: ${options.type})`));
|
|
1885
|
+
if (result.created_entries && result.created_entries.length > 0) {
|
|
1886
|
+
console.log(chalk_1.default.gray(` 초기 KB entry ${result.created_entries.length}건 생성:`));
|
|
1887
|
+
for (const e of result.created_entries) {
|
|
1888
|
+
console.log(chalk_1.default.gray(` - ${e.key}`));
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
console.log(chalk_1.default.gray(`\n 다음 단계: semo kb upsert ${options.domain} <key> --content "..." 으로 데이터 입력\n`));
|
|
1892
|
+
}
|
|
1893
|
+
else {
|
|
1894
|
+
console.log(chalk_1.default.red(`\n❌ 등록 실패: ${result.error}\n`));
|
|
1895
|
+
process.exit(1);
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1849
1899
|
else {
|
|
1850
|
-
console.log(chalk_1.default.red(`알 수 없는 action: '${action}'. 사용 가능: list, show, services, types, instances, schema, routing-table`));
|
|
1900
|
+
console.log(chalk_1.default.red(`알 수 없는 action: '${action}'. 사용 가능: list, show, services, types, instances, schema, routing-table, register`));
|
|
1851
1901
|
process.exit(1);
|
|
1852
1902
|
}
|
|
1853
1903
|
await (0, database_1.closeConnection)();
|
|
@@ -2027,6 +2077,54 @@ ontoCmd
|
|
|
2027
2077
|
process.exit(1);
|
|
2028
2078
|
}
|
|
2029
2079
|
});
|
|
2080
|
+
ontoCmd
|
|
2081
|
+
.command("register <domain>")
|
|
2082
|
+
.description("새 온톨로지 도메인 등록")
|
|
2083
|
+
.requiredOption("--type <type>", "엔티티 타입 (예: service, team, person, bot)")
|
|
2084
|
+
.option("--description <text>", "도메인 설명")
|
|
2085
|
+
.option("--service <name>", "서비스 그룹")
|
|
2086
|
+
.option("--tags <tags>", "태그 (쉼표 구분)")
|
|
2087
|
+
.option("--no-init", "필수 KB entry 자동 생성 건너뛰기")
|
|
2088
|
+
.option("--format <type>", "출력 형식 (json|table)", "table")
|
|
2089
|
+
.action(async (domain, options) => {
|
|
2090
|
+
try {
|
|
2091
|
+
const pool = (0, database_1.getPool)();
|
|
2092
|
+
const tags = options.tags ? options.tags.split(",").map((t) => t.trim()) : undefined;
|
|
2093
|
+
const result = await (0, kb_1.ontoRegister)(pool, {
|
|
2094
|
+
domain,
|
|
2095
|
+
entity_type: options.type,
|
|
2096
|
+
description: options.description,
|
|
2097
|
+
service: options.service,
|
|
2098
|
+
tags,
|
|
2099
|
+
init_required: options.init !== false,
|
|
2100
|
+
});
|
|
2101
|
+
if (options.format === "json") {
|
|
2102
|
+
console.log(JSON.stringify(result, null, 2));
|
|
2103
|
+
}
|
|
2104
|
+
else {
|
|
2105
|
+
if (result.success) {
|
|
2106
|
+
console.log(chalk_1.default.green(`\n✅ 도메인 '${domain}' 등록 완료 (타입: ${options.type})`));
|
|
2107
|
+
if (result.created_entries && result.created_entries.length > 0) {
|
|
2108
|
+
console.log(chalk_1.default.gray(` 초기 KB entry ${result.created_entries.length}건 생성:`));
|
|
2109
|
+
for (const e of result.created_entries) {
|
|
2110
|
+
console.log(chalk_1.default.gray(` - ${e.key}`));
|
|
2111
|
+
}
|
|
2112
|
+
}
|
|
2113
|
+
console.log(chalk_1.default.gray(`\n 다음 단계: semo kb upsert ${domain} <key> --content "..." 으로 데이터 입력\n`));
|
|
2114
|
+
}
|
|
2115
|
+
else {
|
|
2116
|
+
console.log(chalk_1.default.red(`\n❌ 등록 실패: ${result.error}\n`));
|
|
2117
|
+
process.exit(1);
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
await (0, database_1.closeConnection)();
|
|
2121
|
+
}
|
|
2122
|
+
catch (err) {
|
|
2123
|
+
console.error(chalk_1.default.red(`등록 실패: ${err}`));
|
|
2124
|
+
await (0, database_1.closeConnection)();
|
|
2125
|
+
process.exit(1);
|
|
2126
|
+
}
|
|
2127
|
+
});
|
|
2030
2128
|
// === 신규 v4 커맨드 그룹 등록 ===
|
|
2031
2129
|
(0, context_1.registerContextCommands)(program);
|
|
2032
2130
|
(0, bots_1.registerBotsCommands)(program);
|
package/dist/kb.d.ts
CHANGED
|
@@ -198,6 +198,31 @@ export declare function ontoListServices(pool: Pool): Promise<ServiceInfo[]>;
|
|
|
198
198
|
* List service instances (entity_type = 'service')
|
|
199
199
|
*/
|
|
200
200
|
export declare function ontoListInstances(pool: Pool): Promise<ServiceInstance[]>;
|
|
201
|
+
export interface OntoRegisterOptions {
|
|
202
|
+
domain: string;
|
|
203
|
+
entity_type: string;
|
|
204
|
+
description?: string;
|
|
205
|
+
service?: string;
|
|
206
|
+
tags?: string[];
|
|
207
|
+
init_required?: boolean;
|
|
208
|
+
}
|
|
209
|
+
export interface OntoRegisterResult {
|
|
210
|
+
success: boolean;
|
|
211
|
+
error?: string;
|
|
212
|
+
created_entries?: Array<{
|
|
213
|
+
key: string;
|
|
214
|
+
sub_key: string;
|
|
215
|
+
}>;
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Register a new ontology domain with optional initial required KB entries.
|
|
219
|
+
*
|
|
220
|
+
* 1. Validate entity_type exists in ontology_types
|
|
221
|
+
* 2. Check domain doesn't already exist
|
|
222
|
+
* 3. INSERT into semo.ontology
|
|
223
|
+
* 4. If init_required (default true), create KB entries for required keys in kb_type_schema
|
|
224
|
+
*/
|
|
225
|
+
export declare function ontoRegister(pool: Pool, opts: OntoRegisterOptions): Promise<OntoRegisterResult>;
|
|
201
226
|
/**
|
|
202
227
|
* Write ontology schemas to local cache
|
|
203
228
|
*/
|
package/dist/kb.js
CHANGED
|
@@ -60,6 +60,7 @@ exports.ontoListSchema = ontoListSchema;
|
|
|
60
60
|
exports.ontoRoutingTable = ontoRoutingTable;
|
|
61
61
|
exports.ontoListServices = ontoListServices;
|
|
62
62
|
exports.ontoListInstances = ontoListInstances;
|
|
63
|
+
exports.ontoRegister = ontoRegister;
|
|
63
64
|
exports.ontoPullToLocal = ontoPullToLocal;
|
|
64
65
|
const fs = __importStar(require("fs"));
|
|
65
66
|
const path = __importStar(require("path"));
|
|
@@ -886,6 +887,79 @@ async function ontoListInstances(pool) {
|
|
|
886
887
|
client.release();
|
|
887
888
|
}
|
|
888
889
|
}
|
|
890
|
+
/**
|
|
891
|
+
* Register a new ontology domain with optional initial required KB entries.
|
|
892
|
+
*
|
|
893
|
+
* 1. Validate entity_type exists in ontology_types
|
|
894
|
+
* 2. Check domain doesn't already exist
|
|
895
|
+
* 3. INSERT into semo.ontology
|
|
896
|
+
* 4. If init_required (default true), create KB entries for required keys in kb_type_schema
|
|
897
|
+
*/
|
|
898
|
+
async function ontoRegister(pool, opts) {
|
|
899
|
+
const client = await pool.connect();
|
|
900
|
+
try {
|
|
901
|
+
// 1. Validate entity_type
|
|
902
|
+
const typeCheck = await client.query("SELECT type_key FROM semo.ontology_types WHERE type_key = $1", [opts.entity_type]);
|
|
903
|
+
if (typeCheck.rows.length === 0) {
|
|
904
|
+
const known = await client.query("SELECT type_key FROM semo.ontology_types ORDER BY type_key");
|
|
905
|
+
const knownTypes = known.rows.map((r) => r.type_key);
|
|
906
|
+
return {
|
|
907
|
+
success: false,
|
|
908
|
+
error: `타입 '${opts.entity_type}'은(는) 존재하지 않습니다. 사용 가능한 타입: [${knownTypes.join(", ")}]`,
|
|
909
|
+
};
|
|
910
|
+
}
|
|
911
|
+
// 2. Check domain doesn't already exist
|
|
912
|
+
const existCheck = await client.query("SELECT domain FROM semo.ontology WHERE domain = $1", [opts.domain]);
|
|
913
|
+
if (existCheck.rows.length > 0) {
|
|
914
|
+
return { success: false, error: `도메인 '${opts.domain}'은(는) 이미 등록되어 있습니다.` };
|
|
915
|
+
}
|
|
916
|
+
// 3. INSERT into ontology
|
|
917
|
+
await client.query(`INSERT INTO semo.ontology (domain, entity_type, description, service, tags, schema)
|
|
918
|
+
VALUES ($1, $2, $3, $4, $5, $6)`, [
|
|
919
|
+
opts.domain,
|
|
920
|
+
opts.entity_type,
|
|
921
|
+
opts.description || null,
|
|
922
|
+
opts.service || "_global",
|
|
923
|
+
opts.tags || [opts.entity_type],
|
|
924
|
+
JSON.stringify({}),
|
|
925
|
+
]);
|
|
926
|
+
// 4. Create required KB entries
|
|
927
|
+
const createdEntries = [];
|
|
928
|
+
const initRequired = opts.init_required !== false;
|
|
929
|
+
if (initRequired) {
|
|
930
|
+
const schemaResult = await client.query(`SELECT scheme_key, scheme_description, COALESCE(key_type, 'singleton') as key_type, value_hint
|
|
931
|
+
FROM semo.kb_type_schema
|
|
932
|
+
WHERE type_key = $1 AND required = true
|
|
933
|
+
ORDER BY sort_order`, [opts.entity_type]);
|
|
934
|
+
for (const s of schemaResult.rows) {
|
|
935
|
+
if (s.key_type === "collection")
|
|
936
|
+
continue; // collection은 sub_key가 필요하므로 스킵
|
|
937
|
+
const placeholder = s.value_hint
|
|
938
|
+
? `(미입력 — hint: ${s.value_hint})`
|
|
939
|
+
: `(미입력)`;
|
|
940
|
+
const text = `${s.scheme_key}: ${placeholder}`;
|
|
941
|
+
const embedding = await generateEmbedding(text);
|
|
942
|
+
const embeddingStr = embedding ? `[${embedding.join(",")}]` : null;
|
|
943
|
+
try {
|
|
944
|
+
await client.query(`INSERT INTO semo.knowledge_base (domain, key, sub_key, content, metadata, created_by, embedding)
|
|
945
|
+
VALUES ($1, $2, '', $3, '{}', 'semo-cli:onto-register', $4::vector)
|
|
946
|
+
ON CONFLICT (domain, key, sub_key) DO NOTHING`, [opts.domain, s.scheme_key, placeholder, embeddingStr]);
|
|
947
|
+
createdEntries.push({ key: s.scheme_key, sub_key: "" });
|
|
948
|
+
}
|
|
949
|
+
catch {
|
|
950
|
+
// 개별 entry 실패는 무시 — 도메인 등록 자체는 성공
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
return { success: true, created_entries: createdEntries };
|
|
955
|
+
}
|
|
956
|
+
catch (err) {
|
|
957
|
+
return { success: false, error: String(err) };
|
|
958
|
+
}
|
|
959
|
+
finally {
|
|
960
|
+
client.release();
|
|
961
|
+
}
|
|
962
|
+
}
|
|
889
963
|
/**
|
|
890
964
|
* Write ontology schemas to local cache
|
|
891
965
|
*/
|