@team-semicolon/semo-cli 4.7.3 → 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 +96 -4
- package/dist/kb.d.ts +25 -0
- package/dist/kb.js +74 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1705,9 +1705,13 @@ kbCmd
|
|
|
1705
1705
|
kbCmd
|
|
1706
1706
|
.command("ontology")
|
|
1707
1707
|
.description("온톨로지 조회 — 도메인/타입/스키마/라우팅 테이블")
|
|
1708
|
-
.option("--action <type>", "
|
|
1709
|
-
.option("--domain <name>", "action=show 시 도메인")
|
|
1710
|
-
.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 자동 생성 건너뛰기")
|
|
1711
1715
|
.option("--format <type>", "출력 형식 (json|table)", "table")
|
|
1712
1716
|
.action(async (options) => {
|
|
1713
1717
|
try {
|
|
@@ -1852,8 +1856,48 @@ kbCmd
|
|
|
1852
1856
|
console.log();
|
|
1853
1857
|
}
|
|
1854
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
|
+
}
|
|
1855
1899
|
else {
|
|
1856
|
-
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`));
|
|
1857
1901
|
process.exit(1);
|
|
1858
1902
|
}
|
|
1859
1903
|
await (0, database_1.closeConnection)();
|
|
@@ -2033,6 +2077,54 @@ ontoCmd
|
|
|
2033
2077
|
process.exit(1);
|
|
2034
2078
|
}
|
|
2035
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
|
+
});
|
|
2036
2128
|
// === 신규 v4 커맨드 그룹 등록 ===
|
|
2037
2129
|
(0, context_1.registerContextCommands)(program);
|
|
2038
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
|
*/
|