leerness 1.9.163 โ 1.9.166
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/CHANGELOG.md +142 -0
- package/README.md +3 -2
- package/bin/harness.js +430 -13
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,147 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.166 โ 2026-05-20
|
|
4
|
+
|
|
5
|
+
**๐ production-ready 76% ๋ง์ผ์คํค โ pc ์กฐ์ bridge MVP (robotjs/nut-tree opt-in).**
|
|
6
|
+
|
|
7
|
+
์์จ ๋ชจ๋ 96 ๋ผ์ด๋. 1.9.163 5๋ฅ๋ ฅ ๋งคํธ๋ฆญ์ค์์ ๋ ๋ฒ์งธ๋ก ๋ฎ์ ์์ญ (PC ์กฐ์ 5%) ์ง์ ๋ณด๊ฐ.
|
|
8
|
+
1.9.165 (web 67%) ์ ์ด์ด 2 ๋ผ์ด๋ ์ฐ์ 5๋ฅ๋ ฅ ์ง์ ๋ณด๊ฐ โ **์ข
ํฉ 67% โ 76% production-ready ์ฒซ ์ง์
.**
|
|
9
|
+
|
|
10
|
+
### Added โ `leerness pc check|click|type|screenshot`
|
|
11
|
+
**์์กด์ฑ 0 ์์น ์ ์ง** โ leerness ์์ฒด์๋ robotjs/nut-tree ๋ฏธํฌํจ. ์ฌ์ฉ์๊ฐ ๋ณ๋ ์ค์น ์ ์๋ detect.
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# 1) robotjs ๋๋ @nut-tree/nut-js ์ค์น (ํ 1)
|
|
15
|
+
npm i -g robotjs # ๋๋
|
|
16
|
+
npm i -g @nut-tree/nut-js
|
|
17
|
+
leerness permissions set full # โ mouse/keyboard ๊ถํ ํ์
|
|
18
|
+
leerness pc check # โ โ robotjs ๋ฐ๊ฒฌ
|
|
19
|
+
|
|
20
|
+
# 2) ํด๋ฆญ / ํ์ดํ / ์คํฌ๋ฆฐ์ท
|
|
21
|
+
leerness pc click 800 400 # ์ขํ ํด๋ฆญ
|
|
22
|
+
leerness pc type "Hello, leerness" # ํค๋ณด๋ ์
๋ ฅ
|
|
23
|
+
leerness pc screenshot --out shot.png # ์คํฌ๋ฆฐ์ท
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### Bridge ํจํด โ opt-in ์์กด์ฑ
|
|
27
|
+
- `_tryLoadPCAutomation()` โ `robotjs` (๋๊ธฐ) / `@nut-tree/nut-js` (๋น๋๊ธฐ) ๋ ๋ค ์๋ + npm ๊ธ๋ก๋ฒ root ํด๋ฐฑ
|
|
28
|
+
- ๋ฏธ์ค์น ์ ์น์ ํ ์๋ด (`npm i -g robotjs` ๋๋ `npm i -g @nut-tree/nut-js`)
|
|
29
|
+
- `permissionCheck(root, 'mouse'/'keyboard')` ํตํฉ (1.9.146 ๊ถํ ์์คํ
)
|
|
30
|
+
- `_recordRun(kind: 'pc_click' | 'pc_type' | 'pc_screenshot')` observability
|
|
31
|
+
- ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ถ๊ธฐ: robotjs `moveMouse/mouseClick/typeString` (sync), @nut-tree `mouse.move/leftClick/keyboard.type` (async)
|
|
32
|
+
|
|
33
|
+
### 5๋ฅ๋ ฅ ๋งคํธ๋ฆญ์ค ๊ฐฑ์ (๐ production-ready ์ฒซ ์ง์
)
|
|
34
|
+
| ์์ญ | 1.9.165 | **1.9.166** |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| (1) ์น ์๋ํ | 50% โ (bridge) | 50% โ (bridge, playwright ๋ฏธ์ค์น) |
|
|
37
|
+
| (2) **PC ์กฐ์** | **5% โ** | **50% โ ** (bridge MVP, robotjs ๋ฏธ์ค์น) โ **90% โ** (์ฌ์ฉ์ ์ค์น ์) |
|
|
38
|
+
| **์ข
ํฉ** | 67% (beta-ready) | **76% ๐ production-ready** |
|
|
39
|
+
|
|
40
|
+
`leerness health` ๊ฐ ์ค์๊ฐ detect โ `require('robotjs')` ๋๋ `require('@nut-tree/nut-js')` try ์ฑ๊ณต ์ 90% ์๋ ๋ถ์ฌ.
|
|
41
|
+
|
|
42
|
+
### Verified
|
|
43
|
+
- e2e 217/217 โ (1.9.165 baseline)
|
|
44
|
+
- stress-v111: 20/20 (bridge ํจ์ 6์ข
+ CLI ๋์ 5์ข
+ ๋งคํธ๋ฆญ์ค ๊ฐฑ์ 3์ข
+ ๋์ ํ๊ท 6์ข
)
|
|
45
|
+
- VERSION = 1.9.166 / autonomous-rounds = 96
|
|
46
|
+
|
|
47
|
+
### main ์๋ push 27 ๋ผ์ด๋ ์ฐ์
|
|
48
|
+
1.9.140~1.9.166 = 27 ๋ผ์ด๋ release/X.Y.Z + main sync ๋ฌด์ค๋จ.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## 1.9.165 โ 2026-05-20
|
|
53
|
+
|
|
54
|
+
**playwright bridge MVP โ opt-in ์น ์๋ํ (5๋ฅ๋ ฅ #1 ๋ณด๊ฐ, 58% โ 67%).**
|
|
55
|
+
|
|
56
|
+
์์จ ๋ชจ๋ 95 ๋ผ์ด๋. 1.9.163 5๋ฅ๋ ฅ ๋งคํธ๋ฆญ์ค์์ ๊ฐ์ฅ ๋ฎ์ ์์ญ (์น ์๋ํ 5%) ์ง์ ๋ณด๊ฐ.
|
|
57
|
+
|
|
58
|
+
### Added โ `leerness web check|screenshot|extract`
|
|
59
|
+
**์์กด์ฑ 0 ์์น ์ ์ง** โ leerness ์์ฒด์๋ playwright ๋ฏธํฌํจ. ์ฌ์ฉ์๊ฐ `npm i -g playwright` ๋ณ๋ ์ค์น ์ ์๋ detect.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# 1) playwright ์ค์น + ์ฌ์ฉ ๊ฐ๋ฅ ํ์ธ
|
|
63
|
+
npm i -g playwright
|
|
64
|
+
npx playwright install chromium
|
|
65
|
+
leerness permissions set extended # ๋๋ full
|
|
66
|
+
leerness web check # โ โ playwright ๋ฐ๊ฒฌ
|
|
67
|
+
|
|
68
|
+
# 2) ์คํฌ๋ฆฐ์ท
|
|
69
|
+
leerness web screenshot https://example.com --out shot.png
|
|
70
|
+
|
|
71
|
+
# 3) DOM ์ถ์ถ
|
|
72
|
+
leerness web extract https://example.com --selector "h1,h2" --json
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Bridge ํจํด โ opt-in ์์กด์ฑ
|
|
76
|
+
- `_tryLoadPlaywright()` โ `playwright` / `playwright-core` ๋ ๋ค ์๋ + npm ๊ธ๋ก๋ฒ root ํด๋ฐฑ
|
|
77
|
+
- ๋ฏธ์ค์น ์ ์น์ ํ ์๋ด (`npm i -g playwright`)
|
|
78
|
+
- `permissionCheck(root, 'browser')` ํตํฉ (1.9.146 ๊ถํ ์์คํ
)
|
|
79
|
+
- `_recordRun(kind: 'web_screenshot' | 'web_extract')` observability
|
|
80
|
+
|
|
81
|
+
### 5๋ฅ๋ ฅ ๋งคํธ๋ฆญ์ค ๊ฐฑ์
|
|
82
|
+
| ์์ญ | 1.9.164 | **1.9.165** |
|
|
83
|
+
|---|---|---|
|
|
84
|
+
| (1) ์น ์๋ํ | 5% โ | **50% โ ** (bridge MVP, playwright ๋ฏธ์ค์น) โ **90% โ** (์ฌ์ฉ์ ์ค์น ์) |
|
|
85
|
+
| **์ข
ํฉ** | 58% (beta-ready) | **67%** (beta-ready, production-ready ์๋ฐ) |
|
|
86
|
+
|
|
87
|
+
`leerness health` ๊ฐ ์ค์๊ฐ detect โ `require('playwright')` try ์ฑ๊ณต ์ 90% ์๋ ๋ถ์ฌ.
|
|
88
|
+
|
|
89
|
+
### Verified
|
|
90
|
+
- e2e 217/217 โ
|
|
91
|
+
- stress-v110: 20/20 (bridge ํจ์ 6์ข
+ CLI ๋์ 6์ข
+ ๋งคํธ๋ฆญ์ค ๊ฐฑ์ 2์ข
+ ๋์ ํ๊ท 6์ข
)
|
|
92
|
+
- VERSION = 1.9.165 / autonomous-rounds = 95
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 1.9.164 โ 2026-05-20
|
|
97
|
+
|
|
98
|
+
**`leerness which` ์ง๋จ ๋ช
๋ น + REPL provider ์ ํ UX ๊ฐํ (์ฌ์ฉ์ ๋ช
์ 2์ข
).**
|
|
99
|
+
|
|
100
|
+
์์จ ๋ชจ๋ 94 ๋ผ์ด๋.
|
|
101
|
+
|
|
102
|
+
### Added โ `leerness which` (์ฌ์ฉ์ ๋ช
์: ๊ตฌ๋ฒ์ ์ถฉ๋ ํด๊ฒฐ)
|
|
103
|
+
์ฌ์ฉ์๊ฐ "์ต์ ๋ฒ์ ์๋ ์ ํจ" ์์ฌ ์ ํ ๋ฒ์ ์ง๋จ:
|
|
104
|
+
- **ํ์ฌ ์คํ ๊ฒฝ๋ก** โ `__filename` ๊ทธ๋๋ก ํ์ (์ด๋ค leerness ๊ฐ ์คํ๋๋์ง ์ ํํ)
|
|
105
|
+
- **๋ฒ์ / Node / Platform**
|
|
106
|
+
- **`npm root -g`** โ ๊ธ๋ก๋ฒ ์ค์น ๊ฒฝ๋ก
|
|
107
|
+
- **`npm cache`** โ npx ์บ์ฑ ๋๋ ํ ๋ฆฌ
|
|
108
|
+
- **๊ธ๋ก๋ฒ ์ค์น ๋ฒ์ ** โ `npm ls -g leerness`
|
|
109
|
+
- **PATH ํ๋ณด** โ `where`/`which -a` ๊ฒฐ๊ณผ (Windows/Unix ์๋)
|
|
110
|
+
- **์๋ ์ง๋จ** โ ๊ธ๋ก๋ฒ โ ํ์ฌ ์คํ ์ โ ๊ฒฝ๊ณ + ๊ฐ์ ์ต์ ๋ช
๋ น 3์ข
|
|
111
|
+
- `npx --yes leerness@latest <command>`
|
|
112
|
+
- `npm i -g leerness@latest`
|
|
113
|
+
- `npm cache clean --force`
|
|
114
|
+
- `--json` ์ต์
โ ๊ตฌ์กฐํ ์ถ๋ ฅ
|
|
115
|
+
|
|
116
|
+
### Added โ REPL UX: Ollama ์คํจ ์ ๋ค๋ฅธ CLI ์ฆ์ ์ ํ ์๋ด (์ฌ์ฉ์ ๋ช
์)
|
|
117
|
+
- **์์ ์**: Ollama ๋ฏธ๊ฐ๋ + ํ์ฑ ์ธ๋ถ CLI ๋ฐ๊ฒฌ โ ์ฆ์ ๋ฒํธ ์ ํ prompt ์ถ๊ฐ
|
|
118
|
+
- `1) claude 2) codex ...` ํํ๋ก ์ฆ์ ์ ํ ๊ฐ๋ฅ
|
|
119
|
+
- Enter ๋๋ฅด๋ฉด Ollama fallback (๊ธฐ์กด ๋์ ์ ์ง)
|
|
120
|
+
- **๋ฉ์์ง ํธ์ถ ์คํจ ์**: `:provider claude / :provider codex` ์ฆ์ ๊ฐ์ด๋ 1์ค ์ถ๊ฐ
|
|
121
|
+
- ์ฌ์ฉ์ ๋ช
์ "๋ชจ๋ธ์ codex cli ๊ฐ์ ์์ด์ ํธ๋ก ๊ฐํธํ๊ฒ ์ ํ ๊ฐ๋ฅ" ํด๊ฒฐ
|
|
122
|
+
|
|
123
|
+
### Diagnostic ์ค์ ์ฌ์ฉ ์์
|
|
124
|
+
```
|
|
125
|
+
# leerness which (1.9.164)
|
|
126
|
+
ํ์ฌ ์คํ: /usr/local/lib/node_modules/leerness/bin/harness.js
|
|
127
|
+
๋ฒ์ : v1.9.164
|
|
128
|
+
Node: v24.11.1 (linux/x64)
|
|
129
|
+
|
|
130
|
+
## npm ํ๊ฒฝ
|
|
131
|
+
๊ธ๋ก๋ฒ ์ค์น: leerness@1.9.139 โ ์ ๋ฒ์ !
|
|
132
|
+
|
|
133
|
+
## โ ์ง๋จ
|
|
134
|
+
โ ๊ธ๋ก๋ฒ ์ค์น 1.9.139 โ ํ์ฌ ์คํ 1.9.164 โ npx ์บ์ ๋๋ PATH ์ถฉ๋ ์์ฌ
|
|
135
|
+
โ ๊ฐ์ ์ต์ : npm i -g leerness@latest / ๋๋ npx --yes leerness@latest <command>
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Verified
|
|
139
|
+
- e2e 217/217 โ
|
|
140
|
+
- stress-v109: 16/16 (which 6์ข
+ REPL UX 3์ข
+ ๋์ ํ๊ท 7์ข
)
|
|
141
|
+
- VERSION = 1.9.164 / autonomous-rounds = 94
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
3
145
|
## 1.9.163 โ 2026-05-20
|
|
4
146
|
|
|
5
147
|
**`leerness health` ์ 5๋ฅ๋ ฅ ๋งคํธ๋ฆญ์ค ์๋ ํ๊ฐ ํตํฉ.**
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **AI ์ฝ๋ฉ ์์ด์ ํธ์ ๊ฑฐ์ง ์๋ฃยท์ค๋ณตยท๋ง๊ฐยท์ถฉ๋์ ๋ง์์ฃผ๋ ๊ฒ์ยท๊ธฐ์ตยทํ์
CLI ํ๋ค์ค.**
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/leerness) [](https://www.npmjs.com/package/leerness) []() []() []() []() []() []() []() []() []() []() []()
|
|
6
6
|
|
|
7
7
|
```
|
|
8
8
|
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -12,8 +12,9 @@
|
|
|
12
12
|
โ โโโ โโโโโโ โโโโโโ โโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโโโ โ
|
|
13
13
|
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโ โโโโโโโโโโโโโโโโโโโโโโ โ
|
|
14
14
|
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโ โโโโโโ โโโโโโโโโโโโโโโโโโโโโ โ
|
|
15
|
-
โ v1.9.
|
|
15
|
+
โ v1.9.166 AI Agent Reliability Harness + Sandbox โ
|
|
16
16
|
โ verify ยท remember ยท orchestrate ยท audit ยท sandbox ยท drift โ
|
|
17
|
+
โ ๐ 76% production-ready (web + pc bridge opt-in MVP) โ
|
|
17
18
|
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
18
19
|
```
|
|
19
20
|
|
package/bin/harness.js
CHANGED
|
@@ -6,7 +6,7 @@ const path = require('path');
|
|
|
6
6
|
const cp = require('child_process');
|
|
7
7
|
const readline = require('readline');
|
|
8
8
|
|
|
9
|
-
const VERSION = '1.9.
|
|
9
|
+
const VERSION = '1.9.166';
|
|
10
10
|
const MARK = '<!-- leerness:managed -->';
|
|
11
11
|
const README_START = '<!-- leerness:project-readme:start -->';
|
|
12
12
|
const README_END = '<!-- leerness:project-readme:end -->';
|
|
@@ -10769,9 +10769,35 @@ async function _agentRepl(root, opts) {
|
|
|
10769
10769
|
state.model = (idx >= 0 && idx < r.models.length) ? r.models[idx] : r.models[0];
|
|
10770
10770
|
log(C.green(` โ ๋ชจ๋ธ ์ ํ: ${state.model}`));
|
|
10771
10771
|
} else {
|
|
10772
|
-
log(C.yel(` โ Ollama ๋ฏธ๊ฐ๋ ๋๋ ๋ชจ๋ธ
|
|
10773
|
-
|
|
10774
|
-
|
|
10772
|
+
log(C.yel(` โ Ollama ๋ฏธ๊ฐ๋ ๋๋ ๋ชจ๋ธ ์์`));
|
|
10773
|
+
// 1.9.164: Ollama ์คํจ ์ ๋ค๋ฅธ ํ์ฑ CLI ์ฆ์ ์ ์ (UX ๊ฐ์ โ ์ฌ์ฉ์ ๋ช
์ ์์ฒญ)
|
|
10774
|
+
try {
|
|
10775
|
+
const readyCli = EXTERNAL_AGENTS.filter(a => a.id !== 'ollama')
|
|
10776
|
+
.map(a => ({ def: a, status: _checkAgent(a) }))
|
|
10777
|
+
.filter(x => x.status.status === 'ready');
|
|
10778
|
+
if (readyCli.length) {
|
|
10779
|
+
log('');
|
|
10780
|
+
log(C.cy(` ๐ก ํ์ฑ ์ธ๋ถ CLI ${readyCli.length}๊ฐ ๋ฐ๊ฒฌ โ provider ์ ํ ๊ฐ๋ฅ:`));
|
|
10781
|
+
readyCli.forEach((x, i) => log(` ${i + 1}) ${x.def.id} (v${x.status.version || '?'})`));
|
|
10782
|
+
const choice = await new Promise(res => rl.question(C.cy('\n provider ์ ํ (๋ฒํธ / Enter=ollama ๊ณ์): '), res));
|
|
10783
|
+
const idx = parseInt(choice, 10) - 1;
|
|
10784
|
+
if (idx >= 0 && idx < readyCli.length) {
|
|
10785
|
+
state.provider = readyCli[idx].def.id;
|
|
10786
|
+
state.model = null; // ์ provider ๊ธฐ๋ณธ ๋ชจ๋ธ ์ฌ์ฉ
|
|
10787
|
+
log(C.green(` โ provider ์ ํ: ${state.provider} (๋ฉ์์ง ์
๋ ฅ ์ฆ์ ์ฌ์ฉ)`));
|
|
10788
|
+
} else {
|
|
10789
|
+
state.model = process.env.LEERNESS_OLLAMA_MODEL || 'llama3';
|
|
10790
|
+
log(C.dim(` ollama fallback: ${state.model} โ ์ถํ :provider <์ด๋ฆ> ์ผ๋ก ์ ํ ๊ฐ๋ฅ`));
|
|
10791
|
+
}
|
|
10792
|
+
} else {
|
|
10793
|
+
log(C.dim(` ollama serve + ollama pull <model> / ๋๋ .env ์์ LEERNESS_ENABLE_CLAUDE=1 ๋ฑ ํ์ฑํ`));
|
|
10794
|
+
state.model = process.env.LEERNESS_OLLAMA_MODEL || 'llama3';
|
|
10795
|
+
log(C.dim(` fallback: ${state.model} (์ค ํธ์ถ ์คํจ ์ :provider ๋ฉ๋ด ๋๋ :quit)`));
|
|
10796
|
+
}
|
|
10797
|
+
} catch {
|
|
10798
|
+
state.model = process.env.LEERNESS_OLLAMA_MODEL || 'llama3';
|
|
10799
|
+
log(C.dim(` fallback: ${state.model}`));
|
|
10800
|
+
}
|
|
10775
10801
|
}
|
|
10776
10802
|
}
|
|
10777
10803
|
log('');
|
|
@@ -10997,6 +11023,17 @@ async function _agentRepl(root, opts) {
|
|
|
10997
11023
|
if (state.history.length % 6 === 0) saveSession(); // 6ํด๋ง๋ค ์๋ ์ ์ฅ
|
|
10998
11024
|
} else {
|
|
10999
11025
|
log(C.yel(` โ ์คํจ: ${result.error || 'unknown'}`));
|
|
11026
|
+
// 1.9.164: ์คํจ ์ ์ฆ์ ์ ํ ๊ฐ๋ฅ provider ์๋ด (UX ๊ฐ์ โ ์ฌ์ฉ์ ๋ช
์ ์์ฒญ)
|
|
11027
|
+
try {
|
|
11028
|
+
const others = EXTERNAL_AGENTS.filter(a => a.id !== state.provider)
|
|
11029
|
+
.map(a => ({ def: a, status: _checkAgent(a) }))
|
|
11030
|
+
.filter(x => x.status.status === 'ready');
|
|
11031
|
+
if (others.length) {
|
|
11032
|
+
log(C.dim(` ๐ก ์ ํ ๊ฐ๋ฅ: ${others.map(x => `:provider ${x.def.id}`).join(' / ')}`));
|
|
11033
|
+
} else {
|
|
11034
|
+
log(C.dim(` ๐ก ๋ค๋ฅธ provider ํ์ฑํ: .env ์์ LEERNESS_ENABLE_<CLAUDE|CODEX|GEMINI|COPILOT>=1`));
|
|
11035
|
+
}
|
|
11036
|
+
} catch {}
|
|
11000
11037
|
}
|
|
11001
11038
|
rl.prompt();
|
|
11002
11039
|
});
|
|
@@ -11537,14 +11574,31 @@ function healthCmd(root) {
|
|
|
11537
11574
|
try {
|
|
11538
11575
|
const harnessSrc = read(__filename);
|
|
11539
11576
|
const cap = {};
|
|
11540
|
-
// (1) ์น ์๋ํ โ playwright
|
|
11541
|
-
|
|
11542
|
-
|
|
11543
|
-
|
|
11544
|
-
|
|
11545
|
-
|
|
11546
|
-
|
|
11547
|
-
|
|
11577
|
+
// (1) ์น ์๋ํ โ 1.9.165 playwright bridge ํตํฉ + ์ค์ playwright ์ค์น detect
|
|
11578
|
+
const hasWebBridge = /function webCmd\(root, sub/.test(harnessSrc);
|
|
11579
|
+
// ์ฌ์ฉ์๊ฐ playwright ์ค์นํ๋์ง ์ค์๊ฐ detect (require try)
|
|
11580
|
+
let playwrightInstalled = false;
|
|
11581
|
+
try { require('playwright'); playwrightInstalled = true; }
|
|
11582
|
+
catch { try { require('playwright-core'); playwrightInstalled = true; } catch {} }
|
|
11583
|
+
if (hasWebBridge && playwrightInstalled) {
|
|
11584
|
+
cap.webAutomation = { score: 90, status: 'โ', evidence: 'playwright ์ค์น + leerness web bridge (1.9.165)' };
|
|
11585
|
+
} else if (hasWebBridge) {
|
|
11586
|
+
cap.webAutomation = { score: 50, status: 'โ ', evidence: 'leerness web bridge ์์, playwright ๋ฏธ์ค์น (npm i -g playwright)' };
|
|
11587
|
+
} else {
|
|
11588
|
+
cap.webAutomation = { score: 5, status: 'โ', evidence: 'permissions.browser=toggle๋ง (์ค ์ฝ๋ ๋ฏธ๊ตฌํ)' };
|
|
11589
|
+
}
|
|
11590
|
+
// (2) PC ์กฐ์ โ 1.9.166 robotjs/nut-tree bridge + ์ค์ ์ค์น detect
|
|
11591
|
+
const hasPCBridge = /function pcCmd\(root, sub/.test(harnessSrc);
|
|
11592
|
+
let pcInstalled = false;
|
|
11593
|
+
try { require('robotjs'); pcInstalled = true; }
|
|
11594
|
+
catch { try { require('@nut-tree/nut-js'); pcInstalled = true; } catch {} }
|
|
11595
|
+
if (hasPCBridge && pcInstalled) {
|
|
11596
|
+
cap.pcAutomation = { score: 90, status: 'โ', evidence: 'robotjs/nut-tree ์ค์น + leerness pc bridge (1.9.166)' };
|
|
11597
|
+
} else if (hasPCBridge) {
|
|
11598
|
+
cap.pcAutomation = { score: 50, status: 'โ ', evidence: 'leerness pc bridge ์์, robotjs ๋ฏธ์ค์น (npm i -g robotjs)' };
|
|
11599
|
+
} else {
|
|
11600
|
+
cap.pcAutomation = { score: 5, status: 'โ', evidence: 'permissions.mouse/keyboard=ํ๋๋ง (์ค ์ฌ์ฉ์ฒ 0)' };
|
|
11601
|
+
}
|
|
11548
11602
|
// (3) ๋ฉํฐ ์์ด์ ํธ ์ค์ผ์คํธ๋ ์ด์
โ agents multi --execute + consensus ๋ก์ง?
|
|
11549
11603
|
const hasExecute = /const execute = has\('--execute'\)/.test(harnessSrc);
|
|
11550
11604
|
const hasConsensus = /multi-signal consensus/.test(harnessSrc);
|
|
@@ -11884,8 +11938,365 @@ function reuseAutodetectCmd(root) {
|
|
|
11884
11938
|
}
|
|
11885
11939
|
}
|
|
11886
11940
|
|
|
11941
|
+
// 1.9.165: leerness web โ playwright bridge MVP (opt-in ์์กด์ฑ, 5๋ฅ๋ ฅ #1 ๋ณด๊ฐ)
|
|
11942
|
+
// leerness ์์ฒด์๋ playwright ๋ฏธํฌํจ (์์กด์ฑ 0 ์์น ์ ์ง). ์ฌ์ฉ์๊ฐ `npm i -g playwright` ๋ณ๋ ์ค์น ์ ์๋ detect.
|
|
11943
|
+
// permissions.browser=true ํ์ (1.9.146 ๊ถํ ์์คํ
ํตํฉ).
|
|
11944
|
+
function _tryLoadPlaywright() {
|
|
11945
|
+
// ์ฌ์ฉ์ ๊ธ๋ก๋ฒ + ๋ก์ปฌ ๋ชจ๋ ์๋
|
|
11946
|
+
const candidates = ['playwright', 'playwright-core'];
|
|
11947
|
+
for (const id of candidates) {
|
|
11948
|
+
try { return { ok: true, lib: require(id), name: id }; } catch {}
|
|
11949
|
+
}
|
|
11950
|
+
// ๊ธ๋ก๋ฒ npm root ์๋
|
|
11951
|
+
try {
|
|
11952
|
+
const r = cp.spawnSync('npm', ['root', '-g'], { encoding: 'utf8', timeout: 5000, shell: true });
|
|
11953
|
+
if (r.status === 0) {
|
|
11954
|
+
const globalRoot = (r.stdout || '').trim();
|
|
11955
|
+
for (const id of candidates) {
|
|
11956
|
+
try { return { ok: true, lib: require(path.join(globalRoot, id)), name: id, source: 'global' }; } catch {}
|
|
11957
|
+
}
|
|
11958
|
+
}
|
|
11959
|
+
} catch {}
|
|
11960
|
+
return { ok: false, error: 'playwright ๋ฏธ์ค์น โ `npm i -g playwright` ๋๋ ํ๋ก์ ํธ์ `npm i playwright` ํ ๋ค์ ์๋' };
|
|
11961
|
+
}
|
|
11962
|
+
function webCmd(root, sub, ...args) {
|
|
11963
|
+
root = absRoot(root || process.cwd());
|
|
11964
|
+
if (!sub || sub === 'check') {
|
|
11965
|
+
const r = _tryLoadPlaywright();
|
|
11966
|
+
if (has('--json')) {
|
|
11967
|
+
log(JSON.stringify({ installed: r.ok, name: r.name || null, source: r.source || 'local', error: r.error || null, permissions: _readPermissions(root).browser || false }, null, 2));
|
|
11968
|
+
return;
|
|
11969
|
+
}
|
|
11970
|
+
log(`# leerness web check (1.9.165)`);
|
|
11971
|
+
if (r.ok) {
|
|
11972
|
+
log(`โ playwright ๋ฐ๊ฒฌ: ${r.name}${r.source ? ` (${r.source})` : ''}`);
|
|
11973
|
+
log(` โ leerness web screenshot <url> --out file.png ์ฌ์ฉ ๊ฐ๋ฅ`);
|
|
11974
|
+
} else {
|
|
11975
|
+
log(`โ ${r.error}`);
|
|
11976
|
+
}
|
|
11977
|
+
const perms = _readPermissions(root);
|
|
11978
|
+
log(`permissions.browser: ${perms.browser ? 'โ ํ์ฉ' : 'โ ๊ฑฐ๋ถ (basic ๋ชจ๋)'}`);
|
|
11979
|
+
if (!perms.browser) log(` โ leerness permissions set extended ๋๋ set full`);
|
|
11980
|
+
return;
|
|
11981
|
+
}
|
|
11982
|
+
if (sub === 'screenshot') {
|
|
11983
|
+
const url = args[0] || arg('--url', '');
|
|
11984
|
+
const outPath = arg('--out', '');
|
|
11985
|
+
if (!url) return fail('leerness web screenshot <url> --out <file.png> ํ์');
|
|
11986
|
+
if (!outPath) return fail('--out <file.png> ๊ฒฝ๋ก ํ์');
|
|
11987
|
+
if (!/^https?:\/\//.test(url)) return fail(`URL ํ์ ์ค๋ฅ (http:// ๋๋ https://): ${url}`);
|
|
11988
|
+
if (!permissionCheck(root, 'browser', url)) {
|
|
11989
|
+
return fail(`permissions.browser=false (ํ์ฌ: ${_readPermissions(root).mode}) โ leerness permissions set extended ๋๋ full ๊ถ์ฅ`);
|
|
11990
|
+
}
|
|
11991
|
+
const r = _tryLoadPlaywright();
|
|
11992
|
+
if (!r.ok) { fail(r.error); process.exitCode = 1; return; }
|
|
11993
|
+
const t0 = Date.now();
|
|
11994
|
+
log(`# leerness web screenshot (1.9.165)`);
|
|
11995
|
+
log(`URL: ${url} โ ${outPath}`);
|
|
11996
|
+
return (async () => {
|
|
11997
|
+
let browser;
|
|
11998
|
+
try {
|
|
11999
|
+
const { chromium } = r.lib;
|
|
12000
|
+
if (!chromium) { fail('playwright.chromium ์์ โ `npx playwright install chromium` ํ์'); process.exitCode = 1; return; }
|
|
12001
|
+
browser = await chromium.launch({ headless: true });
|
|
12002
|
+
const page = await browser.newPage();
|
|
12003
|
+
await page.goto(url, { timeout: 30000, waitUntil: 'networkidle' });
|
|
12004
|
+
await page.screenshot({ path: outPath, fullPage: true });
|
|
12005
|
+
await browser.close();
|
|
12006
|
+
const dt = Date.now() - t0;
|
|
12007
|
+
ok(`screenshot ์๋ฃ: ${outPath} (${dt}ms)`);
|
|
12008
|
+
try { _recordRun(root, { kind: 'web_screenshot', url, outPath, durationMs: dt, ok: true }); } catch {}
|
|
12009
|
+
} catch (e) {
|
|
12010
|
+
fail(`screenshot ์คํจ: ${e.message}`);
|
|
12011
|
+
if (browser) try { await browser.close(); } catch {}
|
|
12012
|
+
try { _recordRun(root, { kind: 'web_screenshot', url, durationMs: Date.now() - t0, ok: false, error: e.message }); } catch {}
|
|
12013
|
+
process.exitCode = 1;
|
|
12014
|
+
}
|
|
12015
|
+
})();
|
|
12016
|
+
}
|
|
12017
|
+
if (sub === 'extract') {
|
|
12018
|
+
const url = args[0] || arg('--url', '');
|
|
12019
|
+
const selector = arg('--selector', '');
|
|
12020
|
+
if (!url || !selector) return fail('leerness web extract <url> --selector "css-selector" ํ์');
|
|
12021
|
+
if (!/^https?:\/\//.test(url)) return fail(`URL ํ์ ์ค๋ฅ: ${url}`);
|
|
12022
|
+
if (!permissionCheck(root, 'browser', url)) {
|
|
12023
|
+
return fail(`permissions.browser=false โ leerness permissions set extended ๋๋ full`);
|
|
12024
|
+
}
|
|
12025
|
+
const r = _tryLoadPlaywright();
|
|
12026
|
+
if (!r.ok) { fail(r.error); process.exitCode = 1; return; }
|
|
12027
|
+
const t0 = Date.now();
|
|
12028
|
+
return (async () => {
|
|
12029
|
+
let browser;
|
|
12030
|
+
try {
|
|
12031
|
+
const { chromium } = r.lib;
|
|
12032
|
+
if (!chromium) { fail('playwright.chromium ์์ โ `npx playwright install chromium`'); process.exitCode = 1; return; }
|
|
12033
|
+
browser = await chromium.launch({ headless: true });
|
|
12034
|
+
const page = await browser.newPage();
|
|
12035
|
+
await page.goto(url, { timeout: 30000, waitUntil: 'networkidle' });
|
|
12036
|
+
const elements = await page.$$eval(selector, els => els.slice(0, 50).map(el => el.textContent?.trim() || ''));
|
|
12037
|
+
await browser.close();
|
|
12038
|
+
const dt = Date.now() - t0;
|
|
12039
|
+
const out = { url, selector, count: elements.length, elements, durationMs: dt };
|
|
12040
|
+
if (has('--json')) log(JSON.stringify(out, null, 2));
|
|
12041
|
+
else {
|
|
12042
|
+
log(`# leerness web extract (1.9.165)`);
|
|
12043
|
+
log(`URL: ${url} ยท selector: ${selector} ยท ${elements.length}๊ฐ (${dt}ms)`);
|
|
12044
|
+
elements.slice(0, 20).forEach((t, i) => log(` ${i+1}. ${t.slice(0, 200)}${t.length > 200 ? 'โฆ' : ''}`));
|
|
12045
|
+
}
|
|
12046
|
+
try { _recordRun(root, { kind: 'web_extract', url, selector, count: elements.length, durationMs: dt, ok: true }); } catch {}
|
|
12047
|
+
} catch (e) {
|
|
12048
|
+
fail(`extract ์คํจ: ${e.message}`);
|
|
12049
|
+
if (browser) try { await browser.close(); } catch {}
|
|
12050
|
+
process.exitCode = 1;
|
|
12051
|
+
}
|
|
12052
|
+
})();
|
|
12053
|
+
}
|
|
12054
|
+
fail(`์ ์ ์๋ sub: ${sub} (check / screenshot / extract)`);
|
|
12055
|
+
}
|
|
12056
|
+
|
|
12057
|
+
// 1.9.166: leerness pc โ robotjs/nut-tree bridge MVP (opt-in ์์กด์ฑ, 5๋ฅ๋ ฅ #2 ๋ณด๊ฐ)
|
|
12058
|
+
// leerness ์์ฒด์๋ robotjs ๋ฏธํฌํจ (์์กด์ฑ 0). ์ฌ์ฉ์๊ฐ `npm i -g robotjs` ๋ณ๋ ์ค์น ์ ์๋ detect.
|
|
12059
|
+
// permissions.mouse / .keyboard / .browser ํ์ (1.9.146 ๊ถํ ์์คํ
).
|
|
12060
|
+
// โ full ๋ชจ๋ ๊ถ์ฅ โ IDE ํตํฉ ์ธ์๋ ์ํ์ฑ ๋ช
์.
|
|
12061
|
+
function _tryLoadPCAutomation() {
|
|
12062
|
+
// robotjs ์ฐ์ , fallback @nut-tree/nut-js
|
|
12063
|
+
const candidates = ['robotjs', '@nut-tree/nut-js'];
|
|
12064
|
+
for (const id of candidates) {
|
|
12065
|
+
try { return { ok: true, lib: require(id), name: id }; } catch {}
|
|
12066
|
+
}
|
|
12067
|
+
// ๊ธ๋ก๋ฒ npm root ์๋
|
|
12068
|
+
try {
|
|
12069
|
+
const r = cp.spawnSync('npm', ['root', '-g'], { encoding: 'utf8', timeout: 5000, shell: true });
|
|
12070
|
+
if (r.status === 0) {
|
|
12071
|
+
const globalRoot = (r.stdout || '').trim();
|
|
12072
|
+
for (const id of candidates) {
|
|
12073
|
+
try { return { ok: true, lib: require(path.join(globalRoot, id)), name: id, source: 'global' }; } catch {}
|
|
12074
|
+
}
|
|
12075
|
+
}
|
|
12076
|
+
} catch {}
|
|
12077
|
+
return { ok: false, error: 'robotjs/@nut-tree/nut-js ๋ฏธ์ค์น โ `npm i -g robotjs` ๋๋ `npm i -g @nut-tree/nut-js` ํ ๋ค์ ์๋' };
|
|
12078
|
+
}
|
|
12079
|
+
function pcCmd(root, sub, ...args) {
|
|
12080
|
+
root = absRoot(root || process.cwd());
|
|
12081
|
+
if (!sub || sub === 'check') {
|
|
12082
|
+
const r = _tryLoadPCAutomation();
|
|
12083
|
+
const perms = _readPermissions(root);
|
|
12084
|
+
if (has('--json')) {
|
|
12085
|
+
log(JSON.stringify({
|
|
12086
|
+
installed: r.ok,
|
|
12087
|
+
name: r.name || null,
|
|
12088
|
+
source: r.source || 'local',
|
|
12089
|
+
error: r.error || null,
|
|
12090
|
+
permissions: {
|
|
12091
|
+
mouse: perms.mouse || false,
|
|
12092
|
+
keyboard: perms.keyboard || false,
|
|
12093
|
+
mode: perms.mode || 'basic'
|
|
12094
|
+
}
|
|
12095
|
+
}, null, 2));
|
|
12096
|
+
return;
|
|
12097
|
+
}
|
|
12098
|
+
log(`# leerness pc check (1.9.166)`);
|
|
12099
|
+
if (r.ok) {
|
|
12100
|
+
log(`โ ${r.name} ๋ฐ๊ฒฌ${r.source ? ` (${r.source})` : ''}`);
|
|
12101
|
+
log(` โ leerness pc click / type / screenshot ์ฌ์ฉ ๊ฐ๋ฅ`);
|
|
12102
|
+
} else {
|
|
12103
|
+
log(`โ ${r.error}`);
|
|
12104
|
+
}
|
|
12105
|
+
log('');
|
|
12106
|
+
log(`## ๊ถํ (1.9.146)`);
|
|
12107
|
+
log(` permissions.mouse: ${perms.mouse ? 'โ ํ์ฉ' : 'โ ๊ฑฐ๋ถ'}`);
|
|
12108
|
+
log(` permissions.keyboard: ${perms.keyboard ? 'โ ํ์ฉ' : 'โ ๊ฑฐ๋ถ'}`);
|
|
12109
|
+
log(` ํ์ฌ ๋ชจ๋: ${perms.mode || 'basic'}`);
|
|
12110
|
+
if (!perms.mouse || !perms.keyboard) {
|
|
12111
|
+
log('');
|
|
12112
|
+
log(` ๐ก ํ์ฑํ: leerness permissions set full (โ IDE ํตํฉ ์ธ์๋ ์ํ)`);
|
|
12113
|
+
}
|
|
12114
|
+
return;
|
|
12115
|
+
}
|
|
12116
|
+
if (sub === 'click') {
|
|
12117
|
+
const x = parseInt(args[0] || arg('--x', ''), 10);
|
|
12118
|
+
const y = parseInt(args[1] || arg('--y', ''), 10);
|
|
12119
|
+
if (!Number.isFinite(x) || !Number.isFinite(y)) return fail('leerness pc click <x> <y> ํ์ (์ ์ ์ขํ)');
|
|
12120
|
+
if (!permissionCheck(root, 'mouse', `${x},${y}`)) {
|
|
12121
|
+
return fail(`permissions.mouse=false (ํ์ฌ: ${_readPermissions(root).mode}) โ leerness permissions set full`);
|
|
12122
|
+
}
|
|
12123
|
+
const r = _tryLoadPCAutomation();
|
|
12124
|
+
if (!r.ok) { fail(r.error); process.exitCode = 1; return; }
|
|
12125
|
+
const t0 = Date.now();
|
|
12126
|
+
try {
|
|
12127
|
+
if (r.name === 'robotjs') {
|
|
12128
|
+
r.lib.moveMouse(x, y);
|
|
12129
|
+
r.lib.mouseClick();
|
|
12130
|
+
} else if (r.name === '@nut-tree/nut-js') {
|
|
12131
|
+
// nut-js ๋ ๋น๋๊ธฐ
|
|
12132
|
+
return (async () => {
|
|
12133
|
+
const { mouse, Point, Button } = r.lib;
|
|
12134
|
+
await mouse.move([new Point(x, y)]);
|
|
12135
|
+
await mouse.click(Button.LEFT);
|
|
12136
|
+
const dt = Date.now() - t0;
|
|
12137
|
+
ok(`click (${x}, ${y}) โ ${dt}ms`);
|
|
12138
|
+
try { _recordRun(root, { kind: 'pc_click', x, y, lib: r.name, durationMs: dt, ok: true }); } catch {}
|
|
12139
|
+
})();
|
|
12140
|
+
}
|
|
12141
|
+
const dt = Date.now() - t0;
|
|
12142
|
+
ok(`click (${x}, ${y}) โ ${dt}ms`);
|
|
12143
|
+
try { _recordRun(root, { kind: 'pc_click', x, y, lib: r.name, durationMs: dt, ok: true }); } catch {}
|
|
12144
|
+
} catch (e) {
|
|
12145
|
+
fail(`click ์คํจ: ${e.message}`);
|
|
12146
|
+
try { _recordRun(root, { kind: 'pc_click', x, y, lib: r.name, durationMs: Date.now() - t0, ok: false, error: e.message }); } catch {}
|
|
12147
|
+
process.exitCode = 1;
|
|
12148
|
+
}
|
|
12149
|
+
return;
|
|
12150
|
+
}
|
|
12151
|
+
if (sub === 'type') {
|
|
12152
|
+
const text = args[0] || arg('--text', '');
|
|
12153
|
+
if (!text) return fail('leerness pc type "<text>" ํ์');
|
|
12154
|
+
if (!permissionCheck(root, 'keyboard', text)) {
|
|
12155
|
+
return fail(`permissions.keyboard=false โ leerness permissions set full`);
|
|
12156
|
+
}
|
|
12157
|
+
const r = _tryLoadPCAutomation();
|
|
12158
|
+
if (!r.ok) { fail(r.error); process.exitCode = 1; return; }
|
|
12159
|
+
const t0 = Date.now();
|
|
12160
|
+
try {
|
|
12161
|
+
if (r.name === 'robotjs') {
|
|
12162
|
+
r.lib.typeString(text);
|
|
12163
|
+
} else if (r.name === '@nut-tree/nut-js') {
|
|
12164
|
+
return (async () => {
|
|
12165
|
+
const { keyboard } = r.lib;
|
|
12166
|
+
await keyboard.type(text);
|
|
12167
|
+
const dt = Date.now() - t0;
|
|
12168
|
+
ok(`type ${text.length}์ โ ${dt}ms`);
|
|
12169
|
+
try { _recordRun(root, { kind: 'pc_type', chars: text.length, lib: r.name, durationMs: dt, ok: true }); } catch {}
|
|
12170
|
+
})();
|
|
12171
|
+
}
|
|
12172
|
+
const dt = Date.now() - t0;
|
|
12173
|
+
ok(`type ${text.length}์ โ ${dt}ms`);
|
|
12174
|
+
try { _recordRun(root, { kind: 'pc_type', chars: text.length, lib: r.name, durationMs: dt, ok: true }); } catch {}
|
|
12175
|
+
} catch (e) {
|
|
12176
|
+
fail(`type ์คํจ: ${e.message}`);
|
|
12177
|
+
process.exitCode = 1;
|
|
12178
|
+
}
|
|
12179
|
+
return;
|
|
12180
|
+
}
|
|
12181
|
+
if (sub === 'screenshot') {
|
|
12182
|
+
// OS-level screenshot (robotjs.screen ๋๋ nut-js screen.capture)
|
|
12183
|
+
const outPath = arg('--out', '');
|
|
12184
|
+
if (!outPath) return fail('--out <file.png> ๊ฒฝ๋ก ํ์');
|
|
12185
|
+
if (!permissionCheck(root, 'mouse', outPath)) {
|
|
12186
|
+
// mouse ๊ถํ์ผ๋ก screenshot ๋ ์ ์ด (๋์คํ๋ ์ด ์ ๊ทผ)
|
|
12187
|
+
return fail(`permissions.mouse=false (ํ์ฌ: ${_readPermissions(root).mode}) โ leerness permissions set full`);
|
|
12188
|
+
}
|
|
12189
|
+
const r = _tryLoadPCAutomation();
|
|
12190
|
+
if (!r.ok) { fail(r.error); process.exitCode = 1; return; }
|
|
12191
|
+
const t0 = Date.now();
|
|
12192
|
+
try {
|
|
12193
|
+
if (r.name === 'robotjs') {
|
|
12194
|
+
const img = r.lib.screen.capture();
|
|
12195
|
+
// robotjs ์ raw bitmap โ PNG ๋ณํ ํ์ โ MVP ์์ raw ์ ์ฅ
|
|
12196
|
+
fs.writeFileSync(outPath, Buffer.from(img.image));
|
|
12197
|
+
const dt = Date.now() - t0;
|
|
12198
|
+
ok(`screenshot (raw) โ ${dt}ms ยท ${outPath} (PNG ๋ณํ: pngjs ๋๋ sharp ๋ณ๋)`);
|
|
12199
|
+
try { _recordRun(root, { kind: 'pc_screenshot', outPath, lib: r.name, durationMs: dt, ok: true }); } catch {}
|
|
12200
|
+
} else if (r.name === '@nut-tree/nut-js') {
|
|
12201
|
+
return (async () => {
|
|
12202
|
+
const { screen } = r.lib;
|
|
12203
|
+
const img = await screen.capture(outPath);
|
|
12204
|
+
const dt = Date.now() - t0;
|
|
12205
|
+
ok(`screenshot โ ${dt}ms ยท ${outPath}`);
|
|
12206
|
+
try { _recordRun(root, { kind: 'pc_screenshot', outPath, lib: r.name, durationMs: dt, ok: true }); } catch {}
|
|
12207
|
+
})();
|
|
12208
|
+
}
|
|
12209
|
+
} catch (e) {
|
|
12210
|
+
fail(`screenshot ์คํจ: ${e.message}`);
|
|
12211
|
+
process.exitCode = 1;
|
|
12212
|
+
}
|
|
12213
|
+
return;
|
|
12214
|
+
}
|
|
12215
|
+
fail(`์ ์ ์๋ sub: ${sub} (check / click / type / screenshot)`);
|
|
12216
|
+
}
|
|
12217
|
+
|
|
12218
|
+
// 1.9.164: leerness which โ ์ง๋จ ๋๊ตฌ (๊ตฌ๋ฒ์ ์ถฉ๋ / npx ์บ์ / PATH ์ถฉ๋ ํด๊ฒฐ)
|
|
12219
|
+
// ์ฌ์ฉ์๊ฐ "์ต์ ๋ฒ์ ์๋ ์ ํจ" ์์ฌ ์: ์ค์ ์คํ ์ค์ธ leerness ์ ๊ฒฝ๋ก / ๋ฒ์ / npm ์บ์ / PATH ํ๋ณด ํ์.
|
|
12220
|
+
function whichCmd() {
|
|
12221
|
+
const out = {
|
|
12222
|
+
version: VERSION,
|
|
12223
|
+
runningFrom: __filename,
|
|
12224
|
+
nodeVersion: process.version,
|
|
12225
|
+
platform: process.platform,
|
|
12226
|
+
arch: process.arch,
|
|
12227
|
+
npm: {},
|
|
12228
|
+
pathCandidates: []
|
|
12229
|
+
};
|
|
12230
|
+
// npm root -g (๊ธ๋ก๋ฒ ์ค์น ๊ฒฝ๋ก)
|
|
12231
|
+
try {
|
|
12232
|
+
const r = cp.spawnSync('npm', ['root', '-g'], { encoding: 'utf8', timeout: 5000, shell: true });
|
|
12233
|
+
if (r.status === 0) out.npm.globalRoot = (r.stdout || '').trim();
|
|
12234
|
+
} catch {}
|
|
12235
|
+
// npm cache (npx ์บ์ ๊ฒฝ๋ก)
|
|
12236
|
+
try {
|
|
12237
|
+
const r = cp.spawnSync('npm', ['config', 'get', 'cache'], { encoding: 'utf8', timeout: 5000, shell: true });
|
|
12238
|
+
if (r.status === 0) out.npm.cacheDir = (r.stdout || '').trim();
|
|
12239
|
+
} catch {}
|
|
12240
|
+
// npm ๊ธ๋ก๋ฒ leerness ์ค์น ์ ๋ณด
|
|
12241
|
+
try {
|
|
12242
|
+
const r = cp.spawnSync('npm', ['ls', '-g', 'leerness', '--depth=0', '--json'], { encoding: 'utf8', timeout: 8000, shell: true });
|
|
12243
|
+
if (r.stdout) {
|
|
12244
|
+
try {
|
|
12245
|
+
const j = JSON.parse(r.stdout);
|
|
12246
|
+
if (j.dependencies?.leerness) out.npm.globalInstalled = j.dependencies.leerness.version;
|
|
12247
|
+
} catch {}
|
|
12248
|
+
}
|
|
12249
|
+
} catch {}
|
|
12250
|
+
// PATH ํ๋ณด (Windows: where / Unix: which)
|
|
12251
|
+
try {
|
|
12252
|
+
const isWin = process.platform === 'win32';
|
|
12253
|
+
const tool = isWin ? 'where' : 'which';
|
|
12254
|
+
const r = cp.spawnSync(tool, ['-a', 'leerness'], { encoding: 'utf8', timeout: 5000, shell: true });
|
|
12255
|
+
if (r.stdout) out.pathCandidates = (r.stdout || '').trim().split(/\r?\n/).filter(Boolean);
|
|
12256
|
+
} catch {}
|
|
12257
|
+
// ์ง๋จ: ๊ธ๋ก๋ฒ ์ค์น๋ leerness ์ ํ์ฌ ์คํ ๋ฒ์ ์ด ๋ค๋ฅด๋ฉด ๊ฒฝ๊ณ
|
|
12258
|
+
out.diagnostics = [];
|
|
12259
|
+
if (out.npm.globalInstalled && out.npm.globalInstalled !== VERSION) {
|
|
12260
|
+
out.diagnostics.push(`โ ๊ธ๋ก๋ฒ ์ค์น ${out.npm.globalInstalled} โ ํ์ฌ ์คํ ${VERSION} โ npx ์บ์ ๋๋ PATH ์ถฉ๋ ์์ฌ`);
|
|
12261
|
+
out.diagnostics.push(` โ ๊ฐ์ ์ต์ : npm i -g leerness@latest / ๋๋ npx --yes leerness@latest <command>`);
|
|
12262
|
+
}
|
|
12263
|
+
if (out.pathCandidates.length > 1) {
|
|
12264
|
+
out.diagnostics.push(`โ PATH ์ leerness ๊ฐ ${out.pathCandidates.length}๊ฐ โ ์ฐ์ ์์ ์ถฉ๋ ๊ฐ๋ฅ`);
|
|
12265
|
+
out.diagnostics.push(` โ ๋ช
์์ ๊ฒฝ๋ก ์ฌ์ฉ: ${out.runningFrom}`);
|
|
12266
|
+
}
|
|
12267
|
+
if (has('--json')) { log(JSON.stringify(out, null, 2)); return; }
|
|
12268
|
+
log(`# leerness which (1.9.164)`);
|
|
12269
|
+
log(`ํ์ฌ ์คํ: ${out.runningFrom}`);
|
|
12270
|
+
log(`๋ฒ์ : v${out.version}`);
|
|
12271
|
+
log(`Node: ${out.nodeVersion} (${out.platform}/${out.arch})`);
|
|
12272
|
+
log('');
|
|
12273
|
+
log(`## npm ํ๊ฒฝ`);
|
|
12274
|
+
if (out.npm.globalRoot) log(` npm root -g: ${out.npm.globalRoot}`);
|
|
12275
|
+
if (out.npm.cacheDir) log(` npm cache: ${out.npm.cacheDir} (npx ์ ๋ฒ์ ์ด ์ฌ๊ธฐ ์บ์ฑ โ ์์ฌ ์ \`npm cache clean --force\`)`);
|
|
12276
|
+
if (out.npm.globalInstalled) log(` ๊ธ๋ก๋ฒ ์ค์น: leerness@${out.npm.globalInstalled}`);
|
|
12277
|
+
else log(` ๊ธ๋ก๋ฒ ์ค์น: (์์ โ npx ๋๋ ๋ก์ปฌ ๊ฒฝ๋ก๋ง ์ฌ์ฉ ์ค)`);
|
|
12278
|
+
if (out.pathCandidates.length) {
|
|
12279
|
+
log('');
|
|
12280
|
+
log(`## PATH ํ๋ณด (${out.pathCandidates.length}๊ฐ)`);
|
|
12281
|
+
for (const p of out.pathCandidates) log(` - ${p}`);
|
|
12282
|
+
}
|
|
12283
|
+
if (out.diagnostics.length) {
|
|
12284
|
+
log('');
|
|
12285
|
+
log(`## โ ์ง๋จ`);
|
|
12286
|
+
for (const d of out.diagnostics) log(` ${d}`);
|
|
12287
|
+
} else {
|
|
12288
|
+
log('');
|
|
12289
|
+
log(`โ ์ถฉ๋ ์์ (ํ์ฌ ์คํ ๋ฒ์ = ๊ธ๋ก๋ฒ ์ค์น ๋ฒ์ )`);
|
|
12290
|
+
}
|
|
12291
|
+
log('');
|
|
12292
|
+
log(`๐ก ๊ฐ์ ์ต์ ์คํ ๋ฐฉ๋ฒ:`);
|
|
12293
|
+
log(` 1) npx --yes leerness@latest <command> # npx ์บ์ ๋ฌด์ํ๊ณ ์ต์ ๋ค์ด๋ก๋`);
|
|
12294
|
+
log(` 2) npm i -g leerness@latest # ๊ธ๋ก๋ฒ ์ค์น ๊ฐฑ์ `);
|
|
12295
|
+
log(` 3) npm cache clean --force # npx ์บ์ ๊ฐ์ ๋น์ฐ๊ธฐ (์์ฌ ์)`);
|
|
12296
|
+
}
|
|
12297
|
+
|
|
11887
12298
|
function help() {
|
|
11888
|
-
log(`Leerness v${VERSION}\n\nUsage:\n leerness init [path] [--language auto|ko|en] [--skills recommended|all|a,b]\n leerness migrate [path] [--dry-run] [--force]\n leerness update [path] [--check|--yes|--force|--from <tarball>]\n leerness auto-update install [path]\n leerness status [path]\n leerness verify [path]\n leerness debug [path]\n leerness audit [path]\n leerness check [path]\n leerness scan secrets [path]\n leerness encoding check [path]\n leerness lazy detect [path]\n leerness memory search "query" [--limit 5]\n leerness handoff [path] [--all-apps] [--include p1,p2] [--since 24h|3d] [--compact] [--json] # 1.9.17-22 ์ํฌ์คํ์ด์ค (--compact: LLM ์์คํ
ํ๋กฌํํธ์ฉ 1์ค ์์ฝ)\n leerness orchestrate "<๋ชฉํ>" [--agents N] [--model qwen2.5:7b-instruct] [--retry-on-fail K] # 1.9.22 Ollama opt-in (LEERNESS_OLLAMA_BASE_URL ํ์)\n leerness llm-bench record --score N --model X [--label L] [--tokens T] # 1.9.22 LLM ๋ฒค์น ํ์คํ ๋ฆฌ ๋์ \n leerness deps <capability> [--run-tests] [--json] # 1.9.24 depends-on ์ญ๋ฐฉํฅ ์ถ์ + ์๋ ํ๊ท sweep\n leerness memory search "ํค" [--include-code] # 1.9.25 ์์ค ์ฝ๋ ๋ณธ๋ฌธ๋ ๊ฒ์ (๋ชจ์ ๊ฐ์ง ํต์ฌ)\n leerness brainstorm "์ฃผ์ " [--include-code] # 1.9.25 ์ฝ๋ ๋ณธ๋ฌธ hits ํฌํจ\n leerness register-pending "<์์ฒญ>" [--agent X] [--note Y] # 1.9.25 ๋ค์ค ์ธ์
in-progress ์ฆ์ ๋ฑ๋ก\n leerness optimism-check <T-ID> [--json] # 1.9.26/27 ๋๊ด์ ํ์ ๊ฐ์ง (1.9.27: 10 ์นดํ
๊ณ ๋ฆฌ + URL/๋ฉ์๋ ๋งคํ + ์ ๋ขฐ๋ ์ ์)\n leerness persona list|show <id>|add <id> # 1.9.29 ํ๋ฅด์๋ ์นดํ๋ก๊ทธ (๋ณด์/์ฑ๋ฅ/UX/testing/docs 5์ข
๋ด์ฅ)\n leerness review <file> --persona <id1,id2,...> # 1.9.29 ๋๋ฉ์ธ ํ๋ฅด์๋ ๋ฆฌ๋ทฐ ํ๋กฌํํธ ์๋ ์์ฑ\n leerness agents list|check|quota # 1.9.30/31 ์ธ๋ถ AI CLI ๊ฐ์ฉ์ฑ + quota ์ถ์ (claude/codex/gemini/copilot)\n leerness agents dispatch "<task>" --to <id> # 1.9.30 ํ์ฑ CLI ๋์ ์คํ ๋ช
๋ น ์์ฑ (์ค ํธ์ถ X, ์ฌ์ฉ์ ์คํ)\n leerness agents multi "<task>" [--only c1,c2] [--write] [--execute] [--timeout 60] # 1.9.152/156 ํ์ฑ N๊ฐ ์ผ๊ด dispatch (--execute: ์ค spawn + consensus)\n leerness provider list|add|remove [args] # 1.9.157 Provider Registry โ ์ฌ์ฉ์ ์ ์ CLI provider ๋์ ์ถ๊ฐ (OpenRouter/Bedrock ํก์)\n leerness agents dispatch "<task>" --multi # 1.9.152 multi ๋ชจ๋ alias (๋๋ --to all)\n leerness setup-agents [path] [--yes|--no-setup-agents] # 1.9.32 sub-agent CLI ์ธํฐ๋ํฐ๋ธ ์ค์ (.env + ๋ฏธ์ค์น ์๋ ์ค์น)\n leerness init [path] [--no-stale-check] # 1.9.33 npx ์บ์ ํจ์ โ ์ ๋ฒ์ ์๋ ๊ฒฝ๊ณ (๋๋ ค๋ฉด --no-stale-check)\n leerness contract verify <spec.md> <impl.js> [--json] # 1.9.35 ๋ช
์ธ โ ๊ตฌํ ์ผ์น ๊ฒ์ฌ (ํจ์/ํ๋)\n leerness reuse autodetect [path] [--apply] [--json] # 1.9.35 src/*.js์ module.exports โ reuse-map ํ๋ณด ๋ฑ๋ก\n leerness audit [path] [--fix] # 1.9.35 --fix: session-handoff/current-state ์๋ ๊ฐฑ์ \n leerness verify-claim <T-ID> ... [--strict-claims] # 1.9.26 verify-claim์ ๋๊ด์ ํ์ ์๋ ๊ฒ์ฌ ํตํฉ\n leerness reuse-map [path] [--all-apps] [--include p1,p2] [--strict-elements] [--json] # 1.9.18 ์ค๋ณต/์ ์ฌ์ค๋ณต/depends-on\n leerness verify-claim <T-ID> [--path .] [--run-tests] [--json] # 1.9.18-20 evidence ์๋ ๊ฒ์ฆ (1.9.20: scenes/scripts ๋ฑ ๋๋ฉ์ธ ํด๋ + jest/mocha ํ์ฑ)\n leerness verify-code [path] [--build] [--bench] # 1.9.20 --bench: scripts.bench ์ถ๊ฐ ์คํ + evidence ๋์ \n leerness session close [path]\n leerness route <task-type>\n leerness self check [path]\n leerness readme sync [path]\n leerness consistency check [path]\n leerness consistency merge-design-guide [path]\n leerness plan show|init|add|drop|progress|sync [args]\n leerness task list|add|update|drop|fix-evidence|relink [args]\n leerness skill list|info <name>\n leerness skill learn <id> --doc <url> --command "..." --capability "..." [--note ...]\n leerness skill use <id> [--note ...]\n leerness skill optimize <id> --before "..." --after "..." [--note ...]\n leerness skill remove <id>\n leerness skill consolidate [--threshold 0.3]\n leerness gate [path] # verify+audit+scan+encoding+lazy
|
|
12299
|
+
log(`Leerness v${VERSION}\n\nUsage:\n leerness init [path] [--language auto|ko|en] [--skills recommended|all|a,b]\n leerness migrate [path] [--dry-run] [--force]\n leerness update [path] [--check|--yes|--force|--from <tarball>]\n leerness auto-update install [path]\n leerness status [path]\n leerness verify [path]\n leerness debug [path]\n leerness audit [path]\n leerness check [path]\n leerness scan secrets [path]\n leerness encoding check [path]\n leerness lazy detect [path]\n leerness memory search "query" [--limit 5]\n leerness handoff [path] [--all-apps] [--include p1,p2] [--since 24h|3d] [--compact] [--json] # 1.9.17-22 ์ํฌ์คํ์ด์ค (--compact: LLM ์์คํ
ํ๋กฌํํธ์ฉ 1์ค ์์ฝ)\n leerness orchestrate "<๋ชฉํ>" [--agents N] [--model qwen2.5:7b-instruct] [--retry-on-fail K] # 1.9.22 Ollama opt-in (LEERNESS_OLLAMA_BASE_URL ํ์)\n leerness llm-bench record --score N --model X [--label L] [--tokens T] # 1.9.22 LLM ๋ฒค์น ํ์คํ ๋ฆฌ ๋์ \n leerness deps <capability> [--run-tests] [--json] # 1.9.24 depends-on ์ญ๋ฐฉํฅ ์ถ์ + ์๋ ํ๊ท sweep\n leerness memory search "ํค" [--include-code] # 1.9.25 ์์ค ์ฝ๋ ๋ณธ๋ฌธ๋ ๊ฒ์ (๋ชจ์ ๊ฐ์ง ํต์ฌ)\n leerness brainstorm "์ฃผ์ " [--include-code] # 1.9.25 ์ฝ๋ ๋ณธ๋ฌธ hits ํฌํจ\n leerness register-pending "<์์ฒญ>" [--agent X] [--note Y] # 1.9.25 ๋ค์ค ์ธ์
in-progress ์ฆ์ ๋ฑ๋ก\n leerness optimism-check <T-ID> [--json] # 1.9.26/27 ๋๊ด์ ํ์ ๊ฐ์ง (1.9.27: 10 ์นดํ
๊ณ ๋ฆฌ + URL/๋ฉ์๋ ๋งคํ + ์ ๋ขฐ๋ ์ ์)\n leerness persona list|show <id>|add <id> # 1.9.29 ํ๋ฅด์๋ ์นดํ๋ก๊ทธ (๋ณด์/์ฑ๋ฅ/UX/testing/docs 5์ข
๋ด์ฅ)\n leerness review <file> --persona <id1,id2,...> # 1.9.29 ๋๋ฉ์ธ ํ๋ฅด์๋ ๋ฆฌ๋ทฐ ํ๋กฌํํธ ์๋ ์์ฑ\n leerness agents list|check|quota # 1.9.30/31 ์ธ๋ถ AI CLI ๊ฐ์ฉ์ฑ + quota ์ถ์ (claude/codex/gemini/copilot)\n leerness agents dispatch "<task>" --to <id> # 1.9.30 ํ์ฑ CLI ๋์ ์คํ ๋ช
๋ น ์์ฑ (์ค ํธ์ถ X, ์ฌ์ฉ์ ์คํ)\n leerness agents multi "<task>" [--only c1,c2] [--write] [--execute] [--timeout 60] # 1.9.152/156 ํ์ฑ N๊ฐ ์ผ๊ด dispatch (--execute: ์ค spawn + consensus)\n leerness provider list|add|remove [args] # 1.9.157 Provider Registry โ ์ฌ์ฉ์ ์ ์ CLI provider ๋์ ์ถ๊ฐ (OpenRouter/Bedrock ํก์)\n leerness agents dispatch "<task>" --multi # 1.9.152 multi ๋ชจ๋ alias (๋๋ --to all)\n leerness setup-agents [path] [--yes|--no-setup-agents] # 1.9.32 sub-agent CLI ์ธํฐ๋ํฐ๋ธ ์ค์ (.env + ๋ฏธ์ค์น ์๋ ์ค์น)\n leerness init [path] [--no-stale-check] # 1.9.33 npx ์บ์ ํจ์ โ ์ ๋ฒ์ ์๋ ๊ฒฝ๊ณ (๋๋ ค๋ฉด --no-stale-check)\n leerness which [--json] # 1.9.164 ์ง๋จ: ํ์ฌ ์คํ ๊ฒฝ๋ก/๋ฒ์ + npm ์บ์ + PATH ํ๋ณด (๊ตฌ๋ฒ์ ์ถฉ๋ ํด๊ฒฐ)\n leerness web check|screenshot|extract <url> [--out file.png] [--selector "css"] # 1.9.165 playwright bridge (opt-in: npm i -g playwright + permissions.browser)\n leerness pc check|click|type|screenshot [--x N --y N] [--text "s"] [--out f.png] # 1.9.166 robotjs/nut-tree bridge (opt-in: npm i -g robotjs + permissions.mouse/keyboard, โ full ๋ชจ๋ ๊ถ์ฅ)\n leerness contract verify <spec.md> <impl.js> [--json] # 1.9.35 ๋ช
์ธ โ ๊ตฌํ ์ผ์น ๊ฒ์ฌ (ํจ์/ํ๋)\n leerness reuse autodetect [path] [--apply] [--json] # 1.9.35 src/*.js์ module.exports โ reuse-map ํ๋ณด ๋ฑ๋ก\n leerness audit [path] [--fix] # 1.9.35 --fix: session-handoff/current-state ์๋ ๊ฐฑ์ \n leerness verify-claim <T-ID> ... [--strict-claims] # 1.9.26 verify-claim์ ๋๊ด์ ํ์ ์๋ ๊ฒ์ฌ ํตํฉ\n leerness reuse-map [path] [--all-apps] [--include p1,p2] [--strict-elements] [--json] # 1.9.18 ์ค๋ณต/์ ์ฌ์ค๋ณต/depends-on\n leerness verify-claim <T-ID> [--path .] [--run-tests] [--json] # 1.9.18-20 evidence ์๋ ๊ฒ์ฆ (1.9.20: scenes/scripts ๋ฑ ๋๋ฉ์ธ ํด๋ + jest/mocha ํ์ฑ)\n leerness verify-code [path] [--build] [--bench] # 1.9.20 --bench: scripts.bench ์ถ๊ฐ ์คํ + evidence ๋์ \n leerness session close [path]\n leerness route <task-type>\n leerness self check [path]\n leerness readme sync [path]\n leerness consistency check [path]\n leerness consistency merge-design-guide [path]\n leerness plan show|init|add|drop|progress|sync [args]\n leerness task list|add|update|drop|fix-evidence|relink [args]\n leerness skill list|info <name>\n leerness skill learn <id> --doc <url> --command "..." --capability "..." [--note ...]\n leerness skill use <id> [--note ...]\n leerness skill optimize <id> --before "..." --after "..." [--note ...]\n leerness skill remove <id>\n leerness skill consolidate [--threshold 0.3]\n leerness gate [path] # verify+audit+scan+encoding+lazy
|
|
11889
12300
|
leerness retro [path] [--days 7] [--all-apps] [--include p1,p2] [--json] # ํ๊ณ (1.9.13~1.9.16)
|
|
11890
12301
|
leerness insights [path] [--all-apps] [--include p1,p2] [--json] # ๋์ ํต๊ณ (1.9.13~1.9.16)
|
|
11891
12302
|
leerness brainstorm "<์ฃผ์ >" [--all-apps] [--include p1,p2] [--json] # ๋ธ๋ ์ธ์คํ ๋ฐ (1.9.13~1.9.16)
|
|
@@ -11959,6 +12370,12 @@ async function main() {
|
|
|
11959
12370
|
if (cmd === 'agents') return agentsCmd(arg('--path', process.cwd()), args[1], ...args.slice(2));
|
|
11960
12371
|
// 1.9.157: Provider Registry โ ์ฌ์ฉ์ ์ ์ provider ๋์ ์ถ๊ฐ
|
|
11961
12372
|
if (cmd === 'provider') return providerCmd(arg('--path', process.cwd()), args[1], ...args.slice(2));
|
|
12373
|
+
// 1.9.164: leerness which โ ์ง๋จ ๋๊ตฌ (๊ตฌ๋ฒ์ ์ถฉ๋ / npx ์บ์ / PATH ํ๋ณด)
|
|
12374
|
+
if (cmd === 'which') return whichCmd();
|
|
12375
|
+
// 1.9.165: leerness web โ playwright bridge (opt-in ์์กด์ฑ)
|
|
12376
|
+
if (cmd === 'web') return webCmd(arg('--path', process.cwd()), args[1], ...args.slice(2));
|
|
12377
|
+
// 1.9.166: leerness pc โ robotjs/nut-tree bridge (opt-in ์์กด์ฑ)
|
|
12378
|
+
if (cmd === 'pc') return pcCmd(arg('--path', process.cwd()), args[1], ...args.slice(2));
|
|
11962
12379
|
if (cmd === 'contract' && args[1] === 'verify') return contractVerifyCmd(args[2], args[3]);
|
|
11963
12380
|
if (cmd === 'drift' && (args[1] === 'check' || !args[1])) return driftCheckCmd(args[2] || arg('--path', process.cwd()));
|
|
11964
12381
|
if (cmd === 'usage' && (args[1] === 'stats' || !args[1])) return usageStatsCmd(args[2] || arg('--path', process.cwd()));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "leerness",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.166",
|
|
4
4
|
"description": "Leerness: ๋นํ๊ดด ๋ง์ด๊ทธ๋ ์ด์
, ์๋ ๋ฒ์ ๊ฐ์งยท์
๋ฐ์ดํธ, ๊ณํ/์งํ/ํธ๋์คํ ์๋ํ, ๊ฒ์ผ๋ฆยท์ํฌ๋ฆฟยท์ธ์ฝ๋ฉ ์๋ ๊ฐ๋, Claude Code ์ฌ๋์ ํตํฉ์ ๊ฐ์ถ ํ๊ตญ์ด ์ฐ์ AI ๊ฐ๋ฐ ํ๋ค์ค.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"leerness",
|