@mulsok/traders-client 0.1.0
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 +103 -0
- package/bin/cli.js +160 -0
- package/bin/postinstall.js +57 -0
- package/bin/preuninstall.js +36 -0
- package/dist/server/broker/kiwoom/cache.js +86 -0
- package/dist/server/broker/kiwoom/cache.js.map +1 -0
- package/dist/server/broker/kiwoom/client.js +256 -0
- package/dist/server/broker/kiwoom/client.js.map +1 -0
- package/dist/server/broker/kiwoom/endpoints/_helpers.js +61 -0
- package/dist/server/broker/kiwoom/endpoints/_helpers.js.map +1 -0
- package/dist/server/broker/kiwoom/endpoints/account.js +448 -0
- package/dist/server/broker/kiwoom/endpoints/account.js.map +1 -0
- package/dist/server/broker/kiwoom/endpoints/detail.js +118 -0
- package/dist/server/broker/kiwoom/endpoints/detail.js.map +1 -0
- package/dist/server/broker/kiwoom/endpoints/investor.js +139 -0
- package/dist/server/broker/kiwoom/endpoints/investor.js.map +1 -0
- package/dist/server/broker/kiwoom/endpoints/order.js +134 -0
- package/dist/server/broker/kiwoom/endpoints/order.js.map +1 -0
- package/dist/server/broker/kiwoom/endpoints/quote.js +165 -0
- package/dist/server/broker/kiwoom/endpoints/quote.js.map +1 -0
- package/dist/server/broker/kiwoom/endpoints/ranking.js +180 -0
- package/dist/server/broker/kiwoom/endpoints/ranking.js.map +1 -0
- package/dist/server/broker/kiwoom/endpoints/sector.js +135 -0
- package/dist/server/broker/kiwoom/endpoints/sector.js.map +1 -0
- package/dist/server/broker/kiwoom/endpoints/theme.js +104 -0
- package/dist/server/broker/kiwoom/endpoints/theme.js.map +1 -0
- package/dist/server/broker/kiwoom/endpoints/universe.js +119 -0
- package/dist/server/broker/kiwoom/endpoints/universe.js.map +1 -0
- package/dist/server/broker/kiwoom/index.js +59 -0
- package/dist/server/broker/kiwoom/index.js.map +1 -0
- package/dist/server/broker/kiwoom/order-tracker.js +353 -0
- package/dist/server/broker/kiwoom/order-tracker.js.map +1 -0
- package/dist/server/broker/kiwoom/price-feed.js +119 -0
- package/dist/server/broker/kiwoom/price-feed.js.map +1 -0
- package/dist/server/broker/kiwoom/rate-limiter.js +97 -0
- package/dist/server/broker/kiwoom/rate-limiter.js.map +1 -0
- package/dist/server/broker/kiwoom/types.js +13 -0
- package/dist/server/broker/kiwoom/types.js.map +1 -0
- package/dist/server/broker/kiwoom/ws/client.js +370 -0
- package/dist/server/broker/kiwoom/ws/client.js.map +1 -0
- package/dist/server/broker/kiwoom/ws/endpoints/condition.js +146 -0
- package/dist/server/broker/kiwoom/ws/endpoints/condition.js.map +1 -0
- package/dist/server/broker/kiwoom/ws/realtime-bus.js +42 -0
- package/dist/server/broker/kiwoom/ws/realtime-bus.js.map +1 -0
- package/dist/server/broker/kiwoom/ws/types.js +19 -0
- package/dist/server/broker/kiwoom/ws/types.js.map +1 -0
- package/dist/server/broker/news.js +34 -0
- package/dist/server/broker/news.js.map +1 -0
- package/dist/server/bundle.js +43 -0
- package/dist/server/bundle.js.map +1 -0
- package/dist/server/calendar/krx-holidays.js +162 -0
- package/dist/server/calendar/krx-holidays.js.map +1 -0
- package/dist/server/config.js +263 -0
- package/dist/server/config.js.map +1 -0
- package/dist/server/db/sqlite.js +252 -0
- package/dist/server/db/sqlite.js.map +1 -0
- package/dist/server/diary/writer.js +266 -0
- package/dist/server/diary/writer.js.map +1 -0
- package/dist/server/index.js +316 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/jobs/universe-sync.js +132 -0
- package/dist/server/jobs/universe-sync.js.map +1 -0
- package/dist/server/jobs/watchdog.js +87 -0
- package/dist/server/jobs/watchdog.js.map +1 -0
- package/dist/server/journal/pnl-stats.js +108 -0
- package/dist/server/journal/pnl-stats.js.map +1 -0
- package/dist/server/journal/telemetry.js +174 -0
- package/dist/server/journal/telemetry.js.map +1 -0
- package/dist/server/journal/trade-journal.js +239 -0
- package/dist/server/journal/trade-journal.js.map +1 -0
- package/dist/server/llm/anthropic.js +98 -0
- package/dist/server/llm/anthropic.js.map +1 -0
- package/dist/server/llm/claude-cli.js +204 -0
- package/dist/server/llm/claude-cli.js.map +1 -0
- package/dist/server/llm/context-builder.js +229 -0
- package/dist/server/llm/context-builder.js.map +1 -0
- package/dist/server/llm/gemini.js +86 -0
- package/dist/server/llm/gemini.js.map +1 -0
- package/dist/server/llm/index.js +36 -0
- package/dist/server/llm/index.js.map +1 -0
- package/dist/server/llm/openai.js +87 -0
- package/dist/server/llm/openai.js.map +1 -0
- package/dist/server/personas/executor.js +318 -0
- package/dist/server/personas/executor.js.map +1 -0
- package/dist/server/personas/loader.js +165 -0
- package/dist/server/personas/loader.js.map +1 -0
- package/dist/server/personas/persona-agent.js +386 -0
- package/dist/server/personas/persona-agent.js.map +1 -0
- package/dist/server/personas/persona-state.js +170 -0
- package/dist/server/personas/persona-state.js.map +1 -0
- package/dist/server/personas/runner.js +162 -0
- package/dist/server/personas/runner.js.map +1 -0
- package/dist/server/personas/schema.js +123 -0
- package/dist/server/personas/schema.js.map +1 -0
- package/dist/server/personas/wake-plan.js +313 -0
- package/dist/server/personas/wake-plan.js.map +1 -0
- package/dist/server/readiness.js +414 -0
- package/dist/server/readiness.js.map +1 -0
- package/dist/server/routes.js +1216 -0
- package/dist/server/routes.js.map +1 -0
- package/dist/server/safety.js +153 -0
- package/dist/server/safety.js.map +1 -0
- package/dist/server/screener/engine.js +856 -0
- package/dist/server/screener/engine.js.map +1 -0
- package/dist/server/server-sync.js +427 -0
- package/dist/server/server-sync.js.map +1 -0
- package/dist/server/signing.js +39 -0
- package/dist/server/signing.js.map +1 -0
- package/dist/server/watchers/condition-watcher.js +519 -0
- package/dist/server/watchers/condition-watcher.js.map +1 -0
- package/dist/server/watchers/types.js +16 -0
- package/dist/server/watchers/types.js.map +1 -0
- package/dist/web/assets/index-62SMpbaf.js +79 -0
- package/dist/web/assets/index-BPLQR0wt.css +1 -0
- package/dist/web/index.html +14 -0
- package/package.json +93 -0
- package/scripts/com.mulsok.traders.client.plist.template +58 -0
- package/scripts/install-daemon.sh +156 -0
- package/scripts/uninstall-daemon.sh +62 -0
package/README.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# @mulsok/traders-client
|
|
2
|
+
|
|
3
|
+
mulsok-traders 데스크톱 클라이언트 — 한국 주식 AI 자율 트레이더 (macOS · claude CLI).
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@mulsok/traders-client)
|
|
6
|
+
|
|
7
|
+
> **베타 1라운드** · macOS only · claude CLI (Claude Code) 권장
|
|
8
|
+
|
|
9
|
+
## 설치
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npm install -g @mulsok/traders-client
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
설치 직후 `postinstall` 이 자동으로:
|
|
16
|
+
|
|
17
|
+
1. macOS LaunchAgent 등록 (`~/Library/LaunchAgents/com.mulsok.traders.client.plist`)
|
|
18
|
+
2. 부팅 시 자동 시작 + 비정상 종료 시 30초 후 재기동
|
|
19
|
+
3. `http://127.0.0.1:5903` 에서 webui 가동
|
|
20
|
+
|
|
21
|
+
## 사전 요구사항
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
node --version # v20.0.0 이상
|
|
25
|
+
claude --version # 2.x 이상 (https://claude.com/claude-code)
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 첫 사용
|
|
29
|
+
|
|
30
|
+
1. 브라우저 → `http://127.0.0.1:5903/`
|
|
31
|
+
2. 「설정」 페이지 6 탭 입력:
|
|
32
|
+
- 구독 (트레이더 받기)
|
|
33
|
+
- AI 모델 (claude CLI 권장)
|
|
34
|
+
- 증권사 (키움 AppKey/Secret · 모의투자 권장)
|
|
35
|
+
- 자본 배정 (미설정 시 계좌 예수금 전체)
|
|
36
|
+
- 디바이스 토큰 (mulsok-traders.vercel.app 에서 발급)
|
|
37
|
+
- 안전망 (긴급 정지 토글)
|
|
38
|
+
3. PersonasPage → 「지금 분석」 한 번 → 이후 watchdog 자동
|
|
39
|
+
|
|
40
|
+
## 명령어
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
mulsok-client status # 데몬 상태 + readiness
|
|
44
|
+
mulsok-client logs # 로그 실시간 tail
|
|
45
|
+
mulsok-client update # 최신 버전 받기 + 자동 재시작
|
|
46
|
+
mulsok-client uninstall # 데몬 제거 (데이터 보존)
|
|
47
|
+
mulsok-client help # 도움말
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## 업데이트
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm update -g @mulsok/traders-client
|
|
54
|
+
# 또는
|
|
55
|
+
mulsok-client update
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
`postinstall` 이 자동으로 데몬 재시작 — 별도 명령 불필요.
|
|
59
|
+
|
|
60
|
+
## 데이터 위치
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
~/.mulsok-traders/
|
|
64
|
+
├── config.json # 설정 (0600)
|
|
65
|
+
├── market.db # SQLite (universe + candles + watchers)
|
|
66
|
+
├── logs/ # 데몬 로그
|
|
67
|
+
├── personas/<slug>/ # 페르소나 메모리
|
|
68
|
+
├── trade-journal/ # 의사결정 + 주문 (월별 jsonl)
|
|
69
|
+
└── diary/<slug>/ # 페르소나별 일기
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## 제거
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# 데몬 + 패키지 제거 (데이터 보존)
|
|
76
|
+
npm uninstall -g @mulsok/traders-client
|
|
77
|
+
|
|
78
|
+
# 데이터까지 완전 삭제 (되돌릴 수 없음)
|
|
79
|
+
rm -rf ~/.mulsok-traders
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## ⚠️ 베타 1R 한계
|
|
83
|
+
|
|
84
|
+
- macOS only (Windows/Linux 추후)
|
|
85
|
+
- **`CLIENT_HOST=0.0.0.0` 으로 바꾸지 마세요** — 인증 미들웨어 미구현
|
|
86
|
+
- 자동 업데이트 X · 사용자가 `mulsok-client update`
|
|
87
|
+
- 장애 대응: 사용자 직접 운영 + 피드백
|
|
88
|
+
|
|
89
|
+
## 문서
|
|
90
|
+
|
|
91
|
+
- [DOC-704 · 베타 빠른 시작](https://github.com/TaeeunJang/mulsok-traders/blob/main/docs/ops/beta-quickstart.md)
|
|
92
|
+
- [DOC-703 · 클라이언트 설정 가이드](https://github.com/TaeeunJang/mulsok-traders/blob/main/docs/ops/client-config-guide.md)
|
|
93
|
+
- [Web 대시보드](https://mulsok-traders.vercel.app)
|
|
94
|
+
|
|
95
|
+
## 보안
|
|
96
|
+
|
|
97
|
+
- 모든 비밀값 (키움 키 · LLM 키 · 디바이스 토큰) 은 `~/.mulsok-traders/config.json` (0600 권한) 에만 저장
|
|
98
|
+
- 서버는 사용자 자격증명·계좌 정보 보관 X (ADR-008 분산 자율 에이전트)
|
|
99
|
+
- 페르소나 프롬프트는 supabase bundle API 로 동적 fetch (HMAC 검증)
|
|
100
|
+
|
|
101
|
+
## License
|
|
102
|
+
|
|
103
|
+
UNLICENSED — 베타 1라운드 · 사용자 운영 책임.
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* mulsok-client CLI
|
|
4
|
+
*
|
|
5
|
+
* 사용:
|
|
6
|
+
* mulsok-client install · 데몬 등록 + 가동 (postinstall 에서 자동 호출)
|
|
7
|
+
* mulsok-client uninstall · 데몬 제거 (데이터 보존)
|
|
8
|
+
* mulsok-client status · 현재 상태 (launchctl + readiness)
|
|
9
|
+
* mulsok-client logs · 로그 실시간 (tail -f)
|
|
10
|
+
* mulsok-client update · npm update + 데몬 재시작
|
|
11
|
+
* mulsok-client help · 이 메시지
|
|
12
|
+
*/
|
|
13
|
+
import { execSync, spawnSync } from "node:child_process";
|
|
14
|
+
import path from "node:path";
|
|
15
|
+
import { fileURLToPath } from "node:url";
|
|
16
|
+
import { existsSync } from "node:fs";
|
|
17
|
+
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const PKG_ROOT = path.resolve(path.dirname(__filename), "..");
|
|
20
|
+
const SCRIPTS_DIR = path.join(PKG_ROOT, "scripts");
|
|
21
|
+
const HOME = process.env.HOME ?? "";
|
|
22
|
+
const LOG_OUT = path.join(HOME, ".mulsok-traders", "logs", "client.out.log");
|
|
23
|
+
const LOG_ERR = path.join(HOME, ".mulsok-traders", "logs", "client.err.log");
|
|
24
|
+
const LABEL = "com.mulsok.traders.client";
|
|
25
|
+
const HEALTH_URL = "http://127.0.0.1:5903/api/health";
|
|
26
|
+
const READINESS_URL = "http://127.0.0.1:5903/api/readiness";
|
|
27
|
+
|
|
28
|
+
const cmd = process.argv[2];
|
|
29
|
+
|
|
30
|
+
function bold(s) { return `\x1b[1m${s}\x1b[0m`; }
|
|
31
|
+
function green(s) { return `\x1b[32m${s}\x1b[0m`; }
|
|
32
|
+
function yellow(s) { return `\x1b[33m${s}\x1b[0m`; }
|
|
33
|
+
function red(s) { return `\x1b[31m${s}\x1b[0m`; }
|
|
34
|
+
|
|
35
|
+
function isMac() {
|
|
36
|
+
return process.platform === "darwin";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function runScript(scriptName) {
|
|
40
|
+
const scriptPath = path.join(SCRIPTS_DIR, scriptName);
|
|
41
|
+
if (!existsSync(scriptPath)) {
|
|
42
|
+
console.error(red(`✗ ${scriptName} 을 찾지 못했습니다 (${scriptPath})`));
|
|
43
|
+
process.exit(1);
|
|
44
|
+
}
|
|
45
|
+
const r = spawnSync("bash", [scriptPath], { stdio: "inherit" });
|
|
46
|
+
process.exit(r.status ?? 0);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function help() {
|
|
50
|
+
console.log(`${bold("mulsok-client")} · mulsok-traders 데스크톱 클라이언트
|
|
51
|
+
|
|
52
|
+
${bold("명령")}
|
|
53
|
+
install 데몬 등록 + 가동 (npm install 시 자동 실행)
|
|
54
|
+
uninstall 데몬 제거 (데이터 보존)
|
|
55
|
+
status launchctl 상태 + readiness 6 게이트
|
|
56
|
+
logs 로그 실시간 tail
|
|
57
|
+
update npm update + 데몬 재시작
|
|
58
|
+
help 이 메시지
|
|
59
|
+
|
|
60
|
+
${bold("운영 위치")}
|
|
61
|
+
설정/데이터: ~/.mulsok-traders/
|
|
62
|
+
로그: ~/.mulsok-traders/logs/client.{out,err}.log
|
|
63
|
+
LaunchAgent: ~/Library/LaunchAgents/${LABEL}.plist
|
|
64
|
+
|
|
65
|
+
${bold("문서")}
|
|
66
|
+
https://github.com/TaeeunJang/mulsok-traders/blob/main/docs/ops/beta-quickstart.md
|
|
67
|
+
`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function status() {
|
|
71
|
+
if (!isMac()) {
|
|
72
|
+
console.log(yellow("⚠ macOS 외 OS 는 미지원 (Windows/Linux 추후)"));
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
console.log(bold("▶ 데몬 상태"));
|
|
76
|
+
try {
|
|
77
|
+
const out = execSync(`launchctl list | grep ${LABEL} || true`).toString().trim();
|
|
78
|
+
console.log(out || " (등록 안 됨)");
|
|
79
|
+
} catch (e) {
|
|
80
|
+
console.log(` launchctl 조회 실패: ${e.message}`);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
console.log(`\n${bold("▶ 헬스체크")}`);
|
|
84
|
+
try {
|
|
85
|
+
const r = execSync(`curl -sf "${HEALTH_URL}"`).toString();
|
|
86
|
+
console.log(green(` ✓ ${HEALTH_URL}`));
|
|
87
|
+
console.log(` ${r.slice(0, 200)}`);
|
|
88
|
+
} catch {
|
|
89
|
+
console.log(red(` ✗ ${HEALTH_URL} 응답 없음`));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
console.log(`\n${bold("▶ readiness")}`);
|
|
93
|
+
try {
|
|
94
|
+
const r = execSync(`curl -s "${READINESS_URL}" | python3 -m json.tool 2>/dev/null || curl -s "${READINESS_URL}"`).toString();
|
|
95
|
+
console.log(r);
|
|
96
|
+
} catch {
|
|
97
|
+
console.log(" (조회 실패)");
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function logs() {
|
|
102
|
+
if (!existsSync(LOG_OUT) && !existsSync(LOG_ERR)) {
|
|
103
|
+
console.log(yellow("⚠ 로그 파일이 아직 없습니다. 데몬 가동 후 다시 시도."));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
console.log(bold("▶ 로그 실시간 (Ctrl+C 종료)"));
|
|
107
|
+
console.log(` ${LOG_OUT}`);
|
|
108
|
+
console.log(` ${LOG_ERR}\n`);
|
|
109
|
+
try {
|
|
110
|
+
spawnSync("tail", ["-f", LOG_OUT, LOG_ERR], { stdio: "inherit" });
|
|
111
|
+
} catch (e) {
|
|
112
|
+
console.error(red(`✗ tail 실패: ${e.message}`));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function update() {
|
|
117
|
+
console.log(bold("▶ npm update"));
|
|
118
|
+
const pkgName = "@mulsok/traders-client";
|
|
119
|
+
try {
|
|
120
|
+
execSync(`npm install -g ${pkgName}@latest`, { stdio: "inherit" });
|
|
121
|
+
console.log(green("✓ 업데이트 완료 — postinstall 이 데몬을 자동 재시작합니다"));
|
|
122
|
+
} catch (e) {
|
|
123
|
+
console.error(red(`✗ 업데이트 실패: ${e.message}`));
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
switch (cmd) {
|
|
129
|
+
case "install":
|
|
130
|
+
if (!isMac()) {
|
|
131
|
+
console.error(red("✗ macOS 외 OS 는 미지원입니다 (Windows/Linux 추후 라운드)."));
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
runScript("install-daemon.sh");
|
|
135
|
+
break;
|
|
136
|
+
case "uninstall":
|
|
137
|
+
if (!isMac()) {
|
|
138
|
+
console.error(red("✗ macOS 외 OS 는 미지원입니다."));
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
runScript("uninstall-daemon.sh");
|
|
142
|
+
break;
|
|
143
|
+
case "status":
|
|
144
|
+
status();
|
|
145
|
+
break;
|
|
146
|
+
case "logs":
|
|
147
|
+
logs();
|
|
148
|
+
break;
|
|
149
|
+
case "update":
|
|
150
|
+
update();
|
|
151
|
+
break;
|
|
152
|
+
case "help":
|
|
153
|
+
case undefined:
|
|
154
|
+
help();
|
|
155
|
+
break;
|
|
156
|
+
default:
|
|
157
|
+
console.error(red(`✗ 알 수 없는 명령: ${cmd}`));
|
|
158
|
+
help();
|
|
159
|
+
process.exit(1);
|
|
160
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* postinstall — npm install 직후 자동 실행.
|
|
4
|
+
*
|
|
5
|
+
* 동작:
|
|
6
|
+
* 1. 환경 사전 점검 (macOS · Node 20+ · CI 환경 skip)
|
|
7
|
+
* 2. dist/server/index.js 존재 확인 (publish 된 tarball 에 포함되어야)
|
|
8
|
+
* 3. install-daemon.sh 호출 → LaunchAgent 등록 + 데몬 가동
|
|
9
|
+
*
|
|
10
|
+
* 실패 시:
|
|
11
|
+
* - graceful exit 0 (npm install 자체는 통과)
|
|
12
|
+
* - 사용자에게 수동 명령 안내: `mulsok-client install`
|
|
13
|
+
*/
|
|
14
|
+
import { spawnSync } from "node:child_process";
|
|
15
|
+
import path from "node:path";
|
|
16
|
+
import { fileURLToPath } from "node:url";
|
|
17
|
+
import { existsSync } from "node:fs";
|
|
18
|
+
|
|
19
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
20
|
+
const PKG_ROOT = path.resolve(path.dirname(__filename), "..");
|
|
21
|
+
const SCRIPT = path.join(PKG_ROOT, "scripts", "install-daemon.sh");
|
|
22
|
+
const DIST_INDEX = path.join(PKG_ROOT, "dist", "server", "index.js");
|
|
23
|
+
|
|
24
|
+
function gray(s) { return `\x1b[90m${s}\x1b[0m`; }
|
|
25
|
+
function yellow(s) { return `\x1b[33m${s}\x1b[0m`; }
|
|
26
|
+
function green(s) { return `\x1b[32m${s}\x1b[0m`; }
|
|
27
|
+
|
|
28
|
+
// CI 환경 또는 nested install 은 skip
|
|
29
|
+
if (process.env.CI || process.env.MULSOK_SKIP_POSTINSTALL === "1") {
|
|
30
|
+
console.log(gray("[mulsok] postinstall skipped (CI 또는 MULSOK_SKIP_POSTINSTALL=1)"));
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// macOS 외 OS — 메시지만 출력하고 통과
|
|
35
|
+
if (process.platform !== "darwin") {
|
|
36
|
+
console.log(yellow("[mulsok] macOS 외 OS 는 데몬 자동 등록을 건너뜁니다 (현재 베타 1라운드는 macOS only)"));
|
|
37
|
+
process.exit(0);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// dist 미포함 — 개발용 install 가능성 (workspace 등). skip.
|
|
41
|
+
if (!existsSync(DIST_INDEX)) {
|
|
42
|
+
console.log(gray("[mulsok] dist/server/index.js 미존재 — 개발용 install 로 간주, 자동 등록 건너뜀"));
|
|
43
|
+
console.log(gray(" 수동 등록: cd apps/client && npm run build && ./scripts/install-daemon.sh"));
|
|
44
|
+
process.exit(0);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// install-daemon.sh 호출
|
|
48
|
+
console.log(green("[mulsok] LaunchAgent 자동 등록 중..."));
|
|
49
|
+
const r = spawnSync("bash", [SCRIPT], { stdio: "inherit" });
|
|
50
|
+
|
|
51
|
+
if ((r.status ?? 0) !== 0) {
|
|
52
|
+
console.log(yellow(`\n[mulsok] 자동 등록 실패 (exit ${r.status}). 수동으로 다시 시도:`));
|
|
53
|
+
console.log(yellow(" mulsok-client install"));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 항상 0 으로 종료 — npm install 자체는 통과
|
|
57
|
+
process.exit(0);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* preuninstall — `npm uninstall -g @mulsok/traders-client` 직전 실행.
|
|
4
|
+
*
|
|
5
|
+
* 동작:
|
|
6
|
+
* - macOS: launchctl bootout + plist 삭제 (uninstall-daemon.sh 호출)
|
|
7
|
+
* - 데이터 (~/.mulsok-traders/) 는 보존
|
|
8
|
+
*
|
|
9
|
+
* 항상 graceful exit 0 — uninstall 자체는 통과.
|
|
10
|
+
*/
|
|
11
|
+
import { spawnSync } from "node:child_process";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
import { existsSync } from "node:fs";
|
|
15
|
+
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
+
const PKG_ROOT = path.resolve(path.dirname(__filename), "..");
|
|
18
|
+
const SCRIPT = path.join(PKG_ROOT, "scripts", "uninstall-daemon.sh");
|
|
19
|
+
|
|
20
|
+
function gray(s) { return `\x1b[90m${s}\x1b[0m`; }
|
|
21
|
+
|
|
22
|
+
if (process.env.CI || process.env.MULSOK_SKIP_PREUNINSTALL === "1") {
|
|
23
|
+
process.exit(0);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (process.platform !== "darwin") {
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (existsSync(SCRIPT)) {
|
|
31
|
+
console.log(gray("[mulsok] LaunchAgent 자동 제거..."));
|
|
32
|
+
spawnSync("bash", [SCRIPT], { stdio: "inherit" });
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 데이터는 보존 — 사용자가 명시적으로 rm -rf ~/.mulsok-traders 해야 삭제됨
|
|
36
|
+
process.exit(0);
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 인메모리 TTL 캐시 (apps/client · 단일 프로세스 OK)
|
|
3
|
+
*
|
|
4
|
+
* 용도:
|
|
5
|
+
* - 같은 종목 현재가를 1초 내 여러 페르소나가 요청 → 1회 호출
|
|
6
|
+
* - 일봉 60초 캐시 (장 중 빈번 요청)
|
|
7
|
+
* - 종목 마스터 24h 캐시
|
|
8
|
+
*
|
|
9
|
+
* TTL 정책 가이드:
|
|
10
|
+
* - 현재가: 1 초
|
|
11
|
+
* - 호가: 1 초
|
|
12
|
+
* - 분봉: 10 초
|
|
13
|
+
* - 일봉: 60 초
|
|
14
|
+
* - 거래량 랭킹: 30 초
|
|
15
|
+
* - 종목 정보: 86400 초 (1 일)
|
|
16
|
+
*
|
|
17
|
+
* 클래스 대신 단순 함수 + Map · 의도적으로 가벼움 유지
|
|
18
|
+
*/
|
|
19
|
+
const store = new Map();
|
|
20
|
+
/** 만료된 엔트리 자동 정리 (lazy · 다음 get 시) */
|
|
21
|
+
function isFresh(entry) {
|
|
22
|
+
return entry.expiresAt > Date.now();
|
|
23
|
+
}
|
|
24
|
+
export function cacheGet(key) {
|
|
25
|
+
const entry = store.get(key);
|
|
26
|
+
if (!entry)
|
|
27
|
+
return undefined;
|
|
28
|
+
if (!isFresh(entry)) {
|
|
29
|
+
store.delete(key);
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
return entry.value;
|
|
33
|
+
}
|
|
34
|
+
export function cacheSet(key, value, ttlSec) {
|
|
35
|
+
store.set(key, { value, expiresAt: Date.now() + ttlSec * 1000 });
|
|
36
|
+
}
|
|
37
|
+
export function cacheDelete(key) {
|
|
38
|
+
store.delete(key);
|
|
39
|
+
}
|
|
40
|
+
/** 패턴 일치 키 모두 삭제 (예: `current-price:*`) · 시뮬에서 종목 invalidate */
|
|
41
|
+
export function cacheDeletePrefix(prefix) {
|
|
42
|
+
for (const k of store.keys()) {
|
|
43
|
+
if (k.startsWith(prefix))
|
|
44
|
+
store.delete(k);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** 현재 캐시 통계 (디버깅용) */
|
|
48
|
+
export function cacheStats() {
|
|
49
|
+
// 만료 정리
|
|
50
|
+
for (const [k, v] of store.entries()) {
|
|
51
|
+
if (!isFresh(v))
|
|
52
|
+
store.delete(k);
|
|
53
|
+
}
|
|
54
|
+
return { size: store.size, keys: Array.from(store.keys()) };
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* cache-or-fetch 헬퍼
|
|
58
|
+
*
|
|
59
|
+
* 사용:
|
|
60
|
+
* const data = await cacheOrFetch(
|
|
61
|
+
* `current-price:${code}`,
|
|
62
|
+
* 1, // TTL 1s
|
|
63
|
+
* () => fetchCurrentPrice(code)
|
|
64
|
+
* );
|
|
65
|
+
*/
|
|
66
|
+
export async function cacheOrFetch(key, ttlSec, fetcher) {
|
|
67
|
+
const cached = cacheGet(key);
|
|
68
|
+
if (cached !== undefined) {
|
|
69
|
+
return { value: cached, cached: true };
|
|
70
|
+
}
|
|
71
|
+
const value = await fetcher();
|
|
72
|
+
cacheSet(key, value, ttlSec);
|
|
73
|
+
return { value, cached: false };
|
|
74
|
+
}
|
|
75
|
+
/** TTL 정책 상수 (호출자가 import 해서 일관 유지) */
|
|
76
|
+
export const TTL = {
|
|
77
|
+
CURRENT_PRICE: 1,
|
|
78
|
+
ORDERBOOK: 1,
|
|
79
|
+
MINUTE_CHART: 10,
|
|
80
|
+
DAILY_CHART: 60,
|
|
81
|
+
RANKING: 30,
|
|
82
|
+
STOCK_INFO: 86400,
|
|
83
|
+
ACCOUNT_DEPOSIT: 5,
|
|
84
|
+
ACCOUNT_HOLDINGS: 5,
|
|
85
|
+
};
|
|
86
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","sourceRoot":"","sources":["../../../../src-server/broker/kiwoom/cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAOH,MAAM,KAAK,GAAG,IAAI,GAAG,EAA+B,CAAC;AAErD,sCAAsC;AACtC,SAAS,OAAO,CAAI,KAAoB;IACtC,OAAO,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,QAAQ,CAAI,GAAW;IACrC,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAA8B,CAAC;IAC1D,IAAI,CAAC,KAAK;QAAE,OAAO,SAAS,CAAC;IAC7B,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACpB,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAClB,OAAO,SAAS,CAAC;IACnB,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC;AACrB,CAAC;AAED,MAAM,UAAU,QAAQ,CAAI,GAAW,EAAE,KAAQ,EAAE,MAAc;IAC/D,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,MAAM,GAAG,IAAI,EAAE,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,GAAW;IACrC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;AACpB,CAAC;AAED,gEAAgE;AAChE,MAAM,UAAU,iBAAiB,CAAC,MAAc;IAC9C,KAAK,MAAM,CAAC,IAAI,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC;QAC7B,IAAI,CAAC,CAAC,UAAU,CAAC,MAAM,CAAC;YAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC;AAED,sBAAsB;AACtB,MAAM,UAAU,UAAU;IACxB,QAAQ;IACR,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;QACrC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;YAAE,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACnC,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;AAC9D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAW,EACX,MAAc,EACd,OAAyB;IAEzB,MAAM,MAAM,GAAG,QAAQ,CAAI,GAAG,CAAC,CAAC;IAChC,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACzC,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,OAAO,EAAE,CAAC;IAC9B,QAAQ,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC;IAC7B,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;AAClC,CAAC;AAED,uCAAuC;AACvC,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,aAAa,EAAE,CAAC;IAChB,SAAS,EAAE,CAAC;IACZ,YAAY,EAAE,EAAE;IAChB,WAAW,EAAE,EAAE;IACf,OAAO,EAAE,EAAE;IACX,UAAU,EAAE,KAAK;IACjB,eAAe,EAAE,CAAC;IAClB,gBAAgB,EAAE,CAAC;CACX,CAAC"}
|