@ksm0709/context 0.0.33 → 0.0.35
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/README.md +14 -1
- package/dist/cli/index.js +105 -46
- package/dist/index.js +4 -5
- package/dist/mcp.js +491 -85
- package/dist/omc/session-start-hook.js +9 -7
- package/dist/omx/index.mjs +86 -28
- package/package.json +4 -5
|
@@ -20,7 +20,7 @@ function resolveContextDir(projectDir) {
|
|
|
20
20
|
// package.json
|
|
21
21
|
var package_default = {
|
|
22
22
|
name: "@ksm0709/context",
|
|
23
|
-
version: "0.0.
|
|
23
|
+
version: "0.0.35",
|
|
24
24
|
author: {
|
|
25
25
|
name: "TaehoKang",
|
|
26
26
|
email: "ksm07091@gmail.com"
|
|
@@ -62,24 +62,23 @@ var package_default = {
|
|
|
62
62
|
"@opencode-ai/plugin": ">=1.0.0"
|
|
63
63
|
},
|
|
64
64
|
dependencies: {
|
|
65
|
-
"@ksm0709/context": "^0.0.33",
|
|
66
65
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
67
66
|
"jsonc-parser": "^3.0.0"
|
|
68
67
|
},
|
|
69
68
|
devDependencies: {
|
|
70
|
-
"@opencode-ai/plugin": "^1.2.10",
|
|
71
69
|
"@eslint/js": "^9.39.1",
|
|
70
|
+
"@opencode-ai/plugin": "^1.2.10",
|
|
72
71
|
"@types/node": "^20.11.5",
|
|
73
72
|
"@typescript-eslint/eslint-plugin": "8.47.0",
|
|
74
73
|
"@typescript-eslint/parser": "8.47.0",
|
|
74
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
75
75
|
"bun-types": "latest",
|
|
76
76
|
eslint: "^9.39.1",
|
|
77
77
|
"eslint-config-prettier": "10.1.8",
|
|
78
78
|
"eslint-plugin-prettier": "^5.1.3",
|
|
79
79
|
prettier: "^3.2.4",
|
|
80
80
|
"typescript-eslint": "^8.47.0",
|
|
81
|
-
vitest: "^3.2.4"
|
|
82
|
-
"@vitest/coverage-v8": "^3.2.4"
|
|
81
|
+
vitest: "^3.2.4"
|
|
83
82
|
}
|
|
84
83
|
};
|
|
85
84
|
|
|
@@ -482,14 +481,17 @@ var STATIC_KNOWLEDGE_CONTEXT = `## Knowledge Context
|
|
|
482
481
|
3. **\uC790\uAE30 \uC5B8\uC5B4** -- \uBCF5\uC0AC-\uBD99\uC5EC\uB123\uAE30\uAC00 \uC544\uB2CC, \uD575\uC2EC\uC744 \uC774\uD574\uD558\uACE0 \uAC04\uACB0\uD558\uAC8C \uC11C\uC220\uD558\uC138\uC694.
|
|
483
482
|
|
|
484
483
|
### MCP Tools
|
|
485
|
-
- **\uC9C0\uC2DD \
|
|
484
|
+
- **\uC9C0\uC2DD \uD0D0\uC0C9**: \`context_mcp_search_knowledge\`\uB85C \uD6C4\uBCF4 \uB178\uD2B8\uB97C \uCC3E\uACE0, \uACB0\uACFC\uC758 title / description / tags / path / score\uB97C \uBA3C\uC800 \uBE44\uAD50\uD558\uC138\uC694.
|
|
485
|
+
- **\uB178\uD2B8 \uC5F4\uAE30**: \`context_mcp_read_knowledge\`\uB85C \uC120\uD0DD\uD55C \uB178\uD2B8\uB97C \uC5F4\uACE0, \uB05D\uC5D0 \uBD99\uB294 related notes \uBA54\uD0C0\uB370\uC774\uD130\uB85C \uB2E4\uC74C \uD0D0\uC0C9 \uACBD\uB85C\uB97C \uC815\uD558\uC138\uC694.
|
|
486
|
+
- **\uB178\uD2B8 \uC791\uC131/\uC218\uC815**: \`context_mcp_create_knowledge_note\`, \`context_mcp_update_knowledge_note\`
|
|
486
487
|
- **\uB370\uC77C\uB9AC \uB178\uD2B8**: \`context_mcp_read_daily_note\`, \`context_mcp_append_daily_note\`
|
|
487
488
|
- **\uC791\uC5C5 \uC644\uB8CC**: \`context_mcp_submit_turn_complete\` (\uC791\uC5C5 \uC885\uB8CC \uC2DC \uD544\uC218 \uD638\uCD9C)
|
|
488
489
|
|
|
489
490
|
### \uC791\uC5C5 \uC804 \uD544\uC218
|
|
490
491
|
- **\uB370\uC77C\uB9AC \uB178\uD2B8 \uD655\uC778**: \uAC00\uC7A5 \uCD5C\uADFC\uC758 \uB370\uC77C\uB9AC \uB178\uD2B8\uB97C \uC77D\uACE0 \uC774\uC804 \uC138\uC158\uC758 \uCEE8\uD14D\uC2A4\uD2B8\uC640 \uBBF8\uD574\uACB0 \uC774\uC288\uB97C \uD30C\uC545\uD558\uC138\uC694.
|
|
491
492
|
- **\uC791\uC5C5 \uC758\uB3C4 \uC120\uC5B8**: \uC791\uC5C5 \uC2DC\uC791 \uC804, \uD604\uC7AC \uC138\uC158\uC758 \uBAA9\uD45C\uC640 \uC791\uC5C5 \uC758\uB3C4\uB97C \uBA85\uD655\uD788 \uD30C\uC545\uD558\uACE0 \uC120\uC5B8\uD558\uC138\uC694.
|
|
492
|
-
- **\
|
|
493
|
+
- **\uBA54\uD0C0\uB370\uC774\uD130 \uC6B0\uC120 \uAC80\uC0C9**: \uBA3C\uC800 \`search_knowledge\`\uB85C \uAD00\uB828 \uD6C4\uBCF4\uB97C \uCC3E\uACE0, \uBA54\uD0C0\uB370\uC774\uD130\uB97C \uBE44\uAD50\uD55C \uB4A4 \uAC00\uC7A5 \uC720\uB825\uD55C \uB178\uD2B8\uB97C \`read_knowledge\`\uB85C \uC5EC\uC138\uC694.
|
|
494
|
+
- **\uAD00\uB828 \uB178\uD2B8 \uCD94\uC801**: \`read_knowledge\` \uB05D\uC758 related notes \uC139\uC158\uC5D0\uC11C \uC5F0\uACB0\uB41C \uB178\uD2B8\uC758 title / description / tags / path\uB97C \uD655\uC778\uD558\uACE0 \uD544\uC694\uC2DC \uC5F0\uC1C4 \uD0D0\uC0C9\uD558\uC138\uC694.
|
|
493
495
|
- \uC9C0\uC2DD \uD30C\uC77C\uC5D0 \uAE30\uB85D\uB41C \uC544\uD0A4\uD14D\uCC98 \uACB0\uC815, \uD328\uD134, \uC81C\uC57D\uC0AC\uD56D\uC744 \uBC18\uB4DC\uC2DC \uB530\uB974\uC138\uC694.
|
|
494
496
|
|
|
495
497
|
### \uAC1C\uBC1C \uC6D0\uCE59
|
package/dist/omx/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/omx/index.ts
|
|
2
|
-
import { existsSync as
|
|
3
|
-
import { join as
|
|
2
|
+
import { existsSync as existsSync7, readFileSync as readFileSync6, unlinkSync } from "node:fs";
|
|
3
|
+
import { join as join7 } from "node:path";
|
|
4
4
|
|
|
5
5
|
// src/constants.ts
|
|
6
6
|
var DEFAULTS = {
|
|
@@ -105,7 +105,7 @@ import { join as join3 } from "node:path";
|
|
|
105
105
|
// package.json
|
|
106
106
|
var package_default = {
|
|
107
107
|
name: "@ksm0709/context",
|
|
108
|
-
version: "0.0.
|
|
108
|
+
version: "0.0.35",
|
|
109
109
|
author: {
|
|
110
110
|
name: "TaehoKang",
|
|
111
111
|
email: "ksm07091@gmail.com"
|
|
@@ -147,24 +147,23 @@ var package_default = {
|
|
|
147
147
|
"@opencode-ai/plugin": ">=1.0.0"
|
|
148
148
|
},
|
|
149
149
|
dependencies: {
|
|
150
|
-
"@ksm0709/context": "^0.0.33",
|
|
151
150
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
152
151
|
"jsonc-parser": "^3.0.0"
|
|
153
152
|
},
|
|
154
153
|
devDependencies: {
|
|
155
|
-
"@opencode-ai/plugin": "^1.2.10",
|
|
156
154
|
"@eslint/js": "^9.39.1",
|
|
155
|
+
"@opencode-ai/plugin": "^1.2.10",
|
|
157
156
|
"@types/node": "^20.11.5",
|
|
158
157
|
"@typescript-eslint/eslint-plugin": "8.47.0",
|
|
159
158
|
"@typescript-eslint/parser": "8.47.0",
|
|
159
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
160
160
|
"bun-types": "latest",
|
|
161
161
|
eslint: "^9.39.1",
|
|
162
162
|
"eslint-config-prettier": "10.1.8",
|
|
163
163
|
"eslint-plugin-prettier": "^5.1.3",
|
|
164
164
|
prettier: "^3.2.4",
|
|
165
165
|
"typescript-eslint": "^8.47.0",
|
|
166
|
-
vitest: "^3.2.4"
|
|
167
|
-
"@vitest/coverage-v8": "^3.2.4"
|
|
166
|
+
vitest: "^3.2.4"
|
|
168
167
|
}
|
|
169
168
|
};
|
|
170
169
|
|
|
@@ -555,6 +554,59 @@ function injectIntoAgentsMd(agentsMdPath, content) {
|
|
|
555
554
|
writeFileAtomically(agentsMdPath, nextContent);
|
|
556
555
|
}
|
|
557
556
|
|
|
557
|
+
// src/shared/codex-settings.ts
|
|
558
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
559
|
+
import { homedir } from "node:os";
|
|
560
|
+
import { isAbsolute, join as join4 } from "node:path";
|
|
561
|
+
var STALE_MOCK_MCP_SERVER_NAME = "mock-mcp";
|
|
562
|
+
var codexConfigPath = join4(homedir(), ".codex", "config.toml");
|
|
563
|
+
function findMcpServerBlockRange(lines, serverName) {
|
|
564
|
+
const header = `[mcp_servers.${serverName}]`;
|
|
565
|
+
const start = lines.findIndex((line) => line.trim() === header);
|
|
566
|
+
if (start === -1) {
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
let end = lines.length;
|
|
570
|
+
for (let index = start + 1;index < lines.length; index += 1) {
|
|
571
|
+
if (lines[index].startsWith("[")) {
|
|
572
|
+
end = index;
|
|
573
|
+
break;
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
return { start, end };
|
|
577
|
+
}
|
|
578
|
+
function resolveFirstArgPath(blockLines) {
|
|
579
|
+
for (const line of blockLines) {
|
|
580
|
+
const match = line.match(/^\s*args\s*=\s*\[\s*["']([^"']+)["']/);
|
|
581
|
+
if (!match) {
|
|
582
|
+
continue;
|
|
583
|
+
}
|
|
584
|
+
return match[1];
|
|
585
|
+
}
|
|
586
|
+
return null;
|
|
587
|
+
}
|
|
588
|
+
function pruneStaleMockMcpServer() {
|
|
589
|
+
if (!existsSync4(codexConfigPath)) {
|
|
590
|
+
return false;
|
|
591
|
+
}
|
|
592
|
+
const content = readFileSync4(codexConfigPath, "utf8");
|
|
593
|
+
const lines = content.split(`
|
|
594
|
+
`);
|
|
595
|
+
const blockRange = findMcpServerBlockRange(lines, STALE_MOCK_MCP_SERVER_NAME);
|
|
596
|
+
if (!blockRange) {
|
|
597
|
+
return false;
|
|
598
|
+
}
|
|
599
|
+
const blockLines = lines.slice(blockRange.start, blockRange.end);
|
|
600
|
+
const firstArgPath = resolveFirstArgPath(blockLines);
|
|
601
|
+
if (!firstArgPath || !isAbsolute(firstArgPath) || existsSync4(firstArgPath)) {
|
|
602
|
+
return false;
|
|
603
|
+
}
|
|
604
|
+
const nextLines = [...lines.slice(0, blockRange.start), ...lines.slice(blockRange.end)];
|
|
605
|
+
writeFileSync3(codexConfigPath, nextLines.join(`
|
|
606
|
+
`), "utf8");
|
|
607
|
+
return true;
|
|
608
|
+
}
|
|
609
|
+
|
|
558
610
|
// src/shared/knowledge-context.ts
|
|
559
611
|
var STATIC_KNOWLEDGE_CONTEXT = `## Knowledge Context
|
|
560
612
|
|
|
@@ -567,14 +619,17 @@ var STATIC_KNOWLEDGE_CONTEXT = `## Knowledge Context
|
|
|
567
619
|
3. **자기 언어** -- 복사-붙여넣기가 아닌, 핵심을 이해하고 간결하게 서술하세요.
|
|
568
620
|
|
|
569
621
|
### MCP Tools
|
|
570
|
-
- **지식
|
|
622
|
+
- **지식 탐색**: \`context_mcp_search_knowledge\`로 후보 노트를 찾고, 결과의 title / description / tags / path / score를 먼저 비교하세요.
|
|
623
|
+
- **노트 열기**: \`context_mcp_read_knowledge\`로 선택한 노트를 열고, 끝에 붙는 related notes 메타데이터로 다음 탐색 경로를 정하세요.
|
|
624
|
+
- **노트 작성/수정**: \`context_mcp_create_knowledge_note\`, \`context_mcp_update_knowledge_note\`
|
|
571
625
|
- **데일리 노트**: \`context_mcp_read_daily_note\`, \`context_mcp_append_daily_note\`
|
|
572
626
|
- **작업 완료**: \`context_mcp_submit_turn_complete\` (작업 종료 시 필수 호출)
|
|
573
627
|
|
|
574
628
|
### 작업 전 필수
|
|
575
629
|
- **데일리 노트 확인**: 가장 최근의 데일리 노트를 읽고 이전 세션의 컨텍스트와 미해결 이슈를 파악하세요.
|
|
576
630
|
- **작업 의도 선언**: 작업 시작 전, 현재 세션의 목표와 작업 의도를 명확히 파악하고 선언하세요.
|
|
577
|
-
-
|
|
631
|
+
- **메타데이터 우선 검색**: 먼저 \`search_knowledge\`로 관련 후보를 찾고, 메타데이터를 비교한 뒤 가장 유력한 노트를 \`read_knowledge\`로 여세요.
|
|
632
|
+
- **관련 노트 추적**: \`read_knowledge\` 끝의 related notes 섹션에서 연결된 노트의 title / description / tags / path를 확인하고 필요시 연쇄 탐색하세요.
|
|
578
633
|
- 지식 파일에 기록된 아키텍처 결정, 패턴, 제약사항을 반드시 따르세요.
|
|
579
634
|
|
|
580
635
|
### 개발 원칙
|
|
@@ -593,32 +648,32 @@ var STATIC_KNOWLEDGE_CONTEXT = `## Knowledge Context
|
|
|
593
648
|
- 필요한 인자: daily_note_update_proof, knowledge_note_proof, quality_check_output, checkpoint_commit_hashes, scope_review_notes`;
|
|
594
649
|
|
|
595
650
|
// src/omx/registry.ts
|
|
596
|
-
import { join as
|
|
597
|
-
import { existsSync as
|
|
651
|
+
import { join as join6, dirname as dirname3 } from "node:path";
|
|
652
|
+
import { existsSync as existsSync6, readFileSync as readFileSync5, writeFileSync as writeFileSync4, mkdirSync as mkdirSync3 } from "node:fs";
|
|
598
653
|
import { execSync } from "node:child_process";
|
|
599
|
-
import { homedir } from "node:os";
|
|
654
|
+
import { homedir as homedir2 } from "node:os";
|
|
600
655
|
|
|
601
656
|
// src/shared/mcp-path.ts
|
|
602
657
|
import { fileURLToPath } from "node:url";
|
|
603
|
-
import { dirname as dirname2, join as
|
|
604
|
-
import { existsSync as
|
|
658
|
+
import { dirname as dirname2, join as join5, resolve } from "node:path";
|
|
659
|
+
import { existsSync as existsSync5 } from "node:fs";
|
|
605
660
|
import { createRequire } from "node:module";
|
|
606
661
|
function resolveMcpPath() {
|
|
607
662
|
try {
|
|
608
663
|
const req = createRequire(import.meta.url);
|
|
609
664
|
const pkgJsonPath = req.resolve("@ksm0709/context/package.json");
|
|
610
665
|
const pkgRoot = dirname2(pkgJsonPath);
|
|
611
|
-
const distMcp =
|
|
612
|
-
if (
|
|
666
|
+
const distMcp = join5(pkgRoot, "dist", "mcp.js");
|
|
667
|
+
if (existsSync5(distMcp))
|
|
613
668
|
return distMcp;
|
|
614
669
|
} catch {}
|
|
615
670
|
const currentFile = fileURLToPath(import.meta.url);
|
|
616
671
|
const currentDir = dirname2(currentFile);
|
|
617
672
|
const distMcpPath = resolve(currentDir, "..", "mcp.js");
|
|
618
|
-
if (
|
|
673
|
+
if (existsSync5(distMcpPath))
|
|
619
674
|
return distMcpPath;
|
|
620
675
|
const srcMcpPath = resolve(currentDir, "..", "mcp.ts");
|
|
621
|
-
if (
|
|
676
|
+
if (existsSync5(srcMcpPath))
|
|
622
677
|
return srcMcpPath;
|
|
623
678
|
return distMcpPath;
|
|
624
679
|
}
|
|
@@ -633,23 +688,23 @@ function resolveBunPath() {
|
|
|
633
688
|
}
|
|
634
689
|
function getRegistryPaths() {
|
|
635
690
|
return [
|
|
636
|
-
|
|
637
|
-
|
|
691
|
+
join6(homedir2(), ".omx", "mcp-registry.json"),
|
|
692
|
+
join6(homedir2(), ".omc", "mcp-registry.json")
|
|
638
693
|
];
|
|
639
694
|
}
|
|
640
695
|
function ensureMcpRegistered(sdkLog) {
|
|
641
696
|
const registryPaths = getRegistryPaths();
|
|
642
697
|
let targetPath = registryPaths[0];
|
|
643
698
|
for (const p of registryPaths) {
|
|
644
|
-
if (
|
|
699
|
+
if (existsSync6(p)) {
|
|
645
700
|
targetPath = p;
|
|
646
701
|
break;
|
|
647
702
|
}
|
|
648
703
|
}
|
|
649
704
|
let registry = {};
|
|
650
|
-
if (
|
|
705
|
+
if (existsSync6(targetPath)) {
|
|
651
706
|
try {
|
|
652
|
-
const content =
|
|
707
|
+
const content = readFileSync5(targetPath, "utf-8");
|
|
653
708
|
registry = JSON.parse(content);
|
|
654
709
|
} catch (e) {
|
|
655
710
|
if (sdkLog) {
|
|
@@ -678,7 +733,7 @@ function ensureMcpRegistered(sdkLog) {
|
|
|
678
733
|
if (changed) {
|
|
679
734
|
try {
|
|
680
735
|
mkdirSync3(dirname3(targetPath), { recursive: true });
|
|
681
|
-
|
|
736
|
+
writeFileSync4(targetPath, JSON.stringify(registry, null, 2), "utf-8");
|
|
682
737
|
if (sdkLog) {
|
|
683
738
|
sdkLog(`[INFO] Registered context-mcp in ${targetPath}`);
|
|
684
739
|
}
|
|
@@ -777,8 +832,11 @@ async function logWarn(sdk, message, meta = {}) {
|
|
|
777
832
|
}
|
|
778
833
|
async function onSessionStart(event, sdk) {
|
|
779
834
|
const projectDir = resolveProjectDir(event);
|
|
835
|
+
if (pruneStaleMockMcpServer()) {
|
|
836
|
+
await sdk.log.info("Removed stale mock-mcp from ~/.codex/config.toml because its target file is missing.");
|
|
837
|
+
}
|
|
780
838
|
scaffoldIfNeeded(projectDir);
|
|
781
|
-
injectIntoAgentsMd(
|
|
839
|
+
injectIntoAgentsMd(join7(projectDir, "AGENTS.md"), STATIC_KNOWLEDGE_CONTEXT);
|
|
782
840
|
await sdk.log.info(`Injected context into AGENTS.md for ${projectDir}`);
|
|
783
841
|
const wasRegistered = ensureMcpRegistered(sdk.log.info);
|
|
784
842
|
if (wasRegistered) {
|
|
@@ -818,9 +876,9 @@ async function onTurnComplete(event, sdk) {
|
|
|
818
876
|
}
|
|
819
877
|
const followupScopeKey = resolveFollowupScopeKey(event);
|
|
820
878
|
let pendingFollowupScopes = typeof sdk.state?.read === "function" ? await sdk.state.read(TURN_END_PENDING_SKIP_KEY, {}) ?? {} : {};
|
|
821
|
-
const workCompleteFile =
|
|
822
|
-
if (
|
|
823
|
-
const content =
|
|
879
|
+
const workCompleteFile = join7(projectDir, DEFAULTS.workCompleteFile);
|
|
880
|
+
if (existsSync7(workCompleteFile)) {
|
|
881
|
+
const content = readFileSync6(workCompleteFile, "utf-8");
|
|
824
882
|
const { sessionId: fileSessionId, turnId: fileTurnId } = parseWorkComplete(content);
|
|
825
883
|
const currentScopeId = event.session_id ?? event.thread_id ?? "";
|
|
826
884
|
if (!fileSessionId || fileSessionId === currentScopeId) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ksm0709/context",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.35",
|
|
4
4
|
"author": {
|
|
5
5
|
"name": "TaehoKang",
|
|
6
6
|
"email": "ksm07091@gmail.com"
|
|
@@ -42,23 +42,22 @@
|
|
|
42
42
|
"@opencode-ai/plugin": ">=1.0.0"
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
|
-
"@ksm0709/context": "^0.0.33",
|
|
46
45
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
47
46
|
"jsonc-parser": "^3.0.0"
|
|
48
47
|
},
|
|
49
48
|
"devDependencies": {
|
|
50
|
-
"@opencode-ai/plugin": "^1.2.10",
|
|
51
49
|
"@eslint/js": "^9.39.1",
|
|
50
|
+
"@opencode-ai/plugin": "^1.2.10",
|
|
52
51
|
"@types/node": "^20.11.5",
|
|
53
52
|
"@typescript-eslint/eslint-plugin": "8.47.0",
|
|
54
53
|
"@typescript-eslint/parser": "8.47.0",
|
|
54
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
55
55
|
"bun-types": "latest",
|
|
56
56
|
"eslint": "^9.39.1",
|
|
57
57
|
"eslint-config-prettier": "10.1.8",
|
|
58
58
|
"eslint-plugin-prettier": "^5.1.3",
|
|
59
59
|
"prettier": "^3.2.4",
|
|
60
60
|
"typescript-eslint": "^8.47.0",
|
|
61
|
-
"vitest": "^3.2.4"
|
|
62
|
-
"@vitest/coverage-v8": "^3.2.4"
|
|
61
|
+
"vitest": "^3.2.4"
|
|
63
62
|
}
|
|
64
63
|
}
|