devez-vibe 1.7.50 → 1.7.52
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/LICENSE +33 -33
- package/README.md +5 -5
- package/bin/dvz.exe +0 -0
- package/bridge/claude-agent-sdk-bridge.mjs +39 -36
- package/install-skills.mjs +21 -16
- package/package.json +4 -2
- package/prepare-search-deps.mjs +42 -0
- package/skills/insane-search/LICENSE +21 -0
- package/skills/insane-search/SKILL.md +378 -0
- package/skills/insane-search/engine/__init__.py +40 -0
- package/skills/insane-search/engine/__main__.py +148 -0
- package/skills/insane-search/engine/bias_check.py +183 -0
- package/skills/insane-search/engine/content_safety.py +155 -0
- package/skills/insane-search/engine/executor.py +419 -0
- package/skills/insane-search/engine/fetch_chain.py +1276 -0
- package/skills/insane-search/engine/learning.py +179 -0
- package/skills/insane-search/engine/observations_log.py +62 -0
- package/skills/insane-search/engine/phase0.py +273 -0
- package/skills/insane-search/engine/safety.py +91 -0
- package/skills/insane-search/engine/templates/nodriver_fetch.py +48 -0
- package/skills/insane-search/engine/templates/package.json +12 -0
- package/skills/insane-search/engine/templates/patchright_fetch.py +49 -0
- package/skills/insane-search/engine/templates/playwright_mobile_chrome.js +115 -0
- package/skills/insane-search/engine/templates/playwright_real_chrome.js +168 -0
- package/skills/insane-search/engine/tests/test_smoke.py +152 -0
- package/skills/insane-search/engine/tests/test_t1_retry.py +149 -0
- package/skills/insane-search/engine/tests/test_t2_rescue.py +311 -0
- package/skills/insane-search/engine/tests/test_t3_markdown.py +137 -0
- package/skills/insane-search/engine/tests/test_t4_maincontent.py +128 -0
- package/skills/insane-search/engine/tests/test_t5_pdfplumber.py +140 -0
- package/skills/insane-search/engine/tests/test_t6_differential.py +92 -0
- package/skills/insane-search/engine/tests/test_t7_browser_gate.py +229 -0
- package/skills/insane-search/engine/tests/test_u1.py +200 -0
- package/skills/insane-search/engine/tests/test_u4.py +145 -0
- package/skills/insane-search/engine/tests/test_u5.py +163 -0
- package/skills/insane-search/engine/tests/test_u7.py +124 -0
- package/skills/insane-search/engine/tests/test_u8.py +216 -0
- package/skills/insane-search/engine/tests/test_u9.py +117 -0
- package/skills/insane-search/engine/tests/test_url_masking.py +123 -0
- package/skills/insane-search/engine/transport.py +310 -0
- package/skills/insane-search/engine/url_masking.py +70 -0
- package/skills/insane-search/engine/url_transforms.py +98 -0
- package/skills/insane-search/engine/validators.py +349 -0
- package/skills/insane-search/engine/waf_detector.py +214 -0
- package/skills/insane-search/engine/waf_profiles.yaml +221 -0
- package/skills/insane-search/engine/x_search.py +289 -0
- package/skills/insane-search/engine/x_search_io.py +46 -0
- package/skills/insane-search/engine/x_search_types.py +42 -0
- package/skills/insane-search/references/cache-archive.md +83 -0
- package/skills/insane-search/references/fallback.md +159 -0
- package/skills/insane-search/references/jina.md +128 -0
- package/skills/insane-search/references/json-api.md +170 -0
- package/skills/insane-search/references/media.md +148 -0
- package/skills/insane-search/references/metadata.md +116 -0
- package/skills/insane-search/references/naver.md +107 -0
- package/skills/insane-search/references/playwright.md +146 -0
- package/skills/insane-search/references/public-api.md +119 -0
- package/skills/insane-search/references/rss.md +128 -0
- package/skills/insane-search/references/tls-impersonate.md +188 -0
- package/skills/insane-search/references/twitter.md +141 -0
- package/skills/insane-search/tests/coverage_battery.py +297 -0
- package/skills/insane-search/tests/live_benchmark/bench.py +100 -0
- package/skills/insane-search/tests/test_x_search.py +251 -0
- package/skills/luna-loop/agents/openai.yaml +7 -7
package/LICENSE
CHANGED
|
@@ -1,33 +1,33 @@
|
|
|
1
|
-
DevezCode source code license
|
|
2
|
-
|
|
3
|
-
The MIT License below applies only to the DevezCode source code and to files
|
|
4
|
-
that are not listed in the exception below.
|
|
5
|
-
|
|
6
|
-
Exceptions:
|
|
7
|
-
|
|
8
|
-
* `Resources/Images/FileTypes/` and `Resources/Licenses/` are Microsoft Visual
|
|
9
|
-
Studio Image Library materials, subject to the included Microsoft terms.
|
|
10
|
-
* `Resources/Images/ShellPresets/` contains third-party brand assets, subject
|
|
11
|
-
to their respective owners' terms.
|
|
12
|
-
|
|
13
|
-
MIT License
|
|
14
|
-
|
|
15
|
-
Copyright (c) 2026 DevezCode contributors
|
|
16
|
-
|
|
17
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
18
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
19
|
-
in the Software without restriction, including without limitation the rights
|
|
20
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
21
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
22
|
-
furnished to do so, subject to the following conditions:
|
|
23
|
-
|
|
24
|
-
The above copyright notice and this permission notice shall be included in all
|
|
25
|
-
copies or substantial portions of the Software.
|
|
26
|
-
|
|
27
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
28
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
29
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
30
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
31
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
32
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
33
|
-
SOFTWARE.
|
|
1
|
+
DevezCode source code license
|
|
2
|
+
|
|
3
|
+
The MIT License below applies only to the DevezCode source code and to files
|
|
4
|
+
that are not listed in the exception below.
|
|
5
|
+
|
|
6
|
+
Exceptions:
|
|
7
|
+
|
|
8
|
+
* `Resources/Images/FileTypes/` and `Resources/Licenses/` are Microsoft Visual
|
|
9
|
+
Studio Image Library materials, subject to the included Microsoft terms.
|
|
10
|
+
* `Resources/Images/ShellPresets/` contains third-party brand assets, subject
|
|
11
|
+
to their respective owners' terms.
|
|
12
|
+
|
|
13
|
+
MIT License
|
|
14
|
+
|
|
15
|
+
Copyright (c) 2026 DevezCode contributors
|
|
16
|
+
|
|
17
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
18
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
19
|
+
in the Software without restriction, including without limitation the rights
|
|
20
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
21
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
22
|
+
furnished to do so, subject to the following conditions:
|
|
23
|
+
|
|
24
|
+
The above copyright notice and this permission notice shall be included in all
|
|
25
|
+
copies or substantial portions of the Software.
|
|
26
|
+
|
|
27
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
28
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
29
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
30
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
31
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
32
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
33
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -10,11 +10,11 @@
|
|
|
10
10
|
npm install -g devez-vibe
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
설치하면 `dvz` 명령을 사용할 수 있습니다.
|
|
14
|
-
|
|
15
|
-
설치 과정에서 `luna-loop` 스킬도 Codex와 Claude의 사용자 스킬 경로에 함께 설치됩니다.
|
|
16
|
-
스킬은 자동 적용되지 않으며 `$luna-loop`로 직접 호출해야 합니다. 실제
|
|
17
|
-
`gpt-5.6-luna` 검증을 사용하려면 Codex provider와 Luna 위임 기능이 연결돼 있어야 합니다.
|
|
13
|
+
설치하면 `dvz` 명령을 사용할 수 있습니다.
|
|
14
|
+
|
|
15
|
+
설치 과정에서 `luna-loop` 스킬도 Codex와 Claude의 사용자 스킬 경로에 함께 설치됩니다.
|
|
16
|
+
스킬은 자동 적용되지 않으며 `$luna-loop`로 직접 호출해야 합니다. 실제
|
|
17
|
+
`gpt-5.6-luna` 검증을 사용하려면 Codex provider와 Luna 위임 기능이 연결돼 있어야 합니다.
|
|
18
18
|
|
|
19
19
|
| 요건 | 값 |
|
|
20
20
|
| --- | --- |
|
package/bin/dvz.exe
CHANGED
|
Binary file
|
|
@@ -700,6 +700,9 @@ function makeOptions(params, sessionId, resume) {
|
|
|
700
700
|
// Task tools left the default surface in SDK 0.3.233. Listing them here
|
|
701
701
|
// keeps the plan panel available without replacing the Claude Code preset.
|
|
702
702
|
allowedTools: [...CLAUDE_TASK_TOOLS],
|
|
703
|
+
// The fixed-model lanes the Planner and Goal Runner roles dispatch. The
|
|
704
|
+
// host owns the definitions so Claude and Codex read the same text.
|
|
705
|
+
...(params.agents ? { agents: params.agents } : {}),
|
|
703
706
|
// DevezVibe keeps per-task controls available through Claude's TaskStop
|
|
704
707
|
// tool, so a turn interrupt must not tear down independent background work.
|
|
705
708
|
perTaskStopAffordance: true,
|
|
@@ -1546,9 +1549,9 @@ function processAssistant(session, message) {
|
|
|
1546
1549
|
capabilities,
|
|
1547
1550
|
message.message?.model,
|
|
1548
1551
|
session.model,
|
|
1549
|
-
);
|
|
1550
|
-
const content = Array.isArray(message.message?.content) ? message.message.content : [];
|
|
1551
|
-
// Without partial SDK events, replay completed text before tool items so the
|
|
1552
|
+
);
|
|
1553
|
+
const content = Array.isArray(message.message?.content) ? message.message.content : [];
|
|
1554
|
+
// Without partial SDK events, replay completed text before tool items so the
|
|
1552
1555
|
// visible order still matches the assistant content order.
|
|
1553
1556
|
if (!session.streamBlocks.size && !session.turn.sawStreamText) {
|
|
1554
1557
|
for (const block of content) {
|
|
@@ -2898,22 +2901,22 @@ function historyState(messages) {
|
|
|
2898
2901
|
}));
|
|
2899
2902
|
turn.items.push({ id: "claude-plan-latest", type: "plan", text, steps });
|
|
2900
2903
|
}
|
|
2901
|
-
return {
|
|
2902
|
-
tasks,
|
|
2903
|
-
turns: turns
|
|
2904
|
-
.filter((candidate) => !candidate.synthetic)
|
|
2905
|
-
.map(({ synthetic: _, ...candidate }) => {
|
|
2906
|
-
const answered = candidate.items.some((item) =>
|
|
2907
|
-
item.type === "agentMessage" && String(item.text || "").trim());
|
|
2908
|
-
// Claude persists the user record before an immediate interrupt. Native
|
|
2909
|
-
// resume hides that dangling prompt, so expose it as interrupted for the
|
|
2910
|
-
// common host history filter instead of calling every transcript turn
|
|
2911
|
-
// completed.
|
|
2912
|
-
if (!answered) candidate.status = "interrupted";
|
|
2913
|
-
return candidate;
|
|
2914
|
-
}),
|
|
2915
|
-
};
|
|
2916
|
-
}
|
|
2904
|
+
return {
|
|
2905
|
+
tasks,
|
|
2906
|
+
turns: turns
|
|
2907
|
+
.filter((candidate) => !candidate.synthetic)
|
|
2908
|
+
.map(({ synthetic: _, ...candidate }) => {
|
|
2909
|
+
const answered = candidate.items.some((item) =>
|
|
2910
|
+
item.type === "agentMessage" && String(item.text || "").trim());
|
|
2911
|
+
// Claude persists the user record before an immediate interrupt. Native
|
|
2912
|
+
// resume hides that dangling prompt, so expose it as interrupted for the
|
|
2913
|
+
// common host history filter instead of calling every transcript turn
|
|
2914
|
+
// completed.
|
|
2915
|
+
if (!answered) candidate.status = "interrupted";
|
|
2916
|
+
return candidate;
|
|
2917
|
+
}),
|
|
2918
|
+
};
|
|
2919
|
+
}
|
|
2917
2920
|
|
|
2918
2921
|
function historyTurns(messages) {
|
|
2919
2922
|
return historyState(messages).turns;
|
|
@@ -3604,15 +3607,15 @@ async function runSelfTest() {
|
|
|
3604
3607
|
["say hello", "claude:claude-sonnet-5"],
|
|
3605
3608
|
["hay zzz", "claude:claude-haiku-4-5-20251001"],
|
|
3606
3609
|
];
|
|
3607
|
-
if (turns.length !== expected.length
|
|
3608
|
-
|| prompts.some((prompt, index) => prompt?.content?.[0]?.text !== expected[index][0]
|
|
3609
|
-
|| prompt.model !== expected[index][1])) {
|
|
3610
|
-
throw new Error(`Claude history self-test failed: ${JSON.stringify(turns)}`);
|
|
3611
|
-
}
|
|
3612
|
-
const interrupted = historyTurns([user("cancelled", "취소한 요청")]);
|
|
3613
|
-
if (interrupted.length !== 1 || interrupted[0].status !== "interrupted") {
|
|
3614
|
-
throw new Error(`Claude interrupted prompt history self-test failed: ${JSON.stringify(interrupted)}`);
|
|
3615
|
-
}
|
|
3610
|
+
if (turns.length !== expected.length
|
|
3611
|
+
|| prompts.some((prompt, index) => prompt?.content?.[0]?.text !== expected[index][0]
|
|
3612
|
+
|| prompt.model !== expected[index][1])) {
|
|
3613
|
+
throw new Error(`Claude history self-test failed: ${JSON.stringify(turns)}`);
|
|
3614
|
+
}
|
|
3615
|
+
const interrupted = historyTurns([user("cancelled", "취소한 요청")]);
|
|
3616
|
+
if (interrupted.length !== 1 || interrupted[0].status !== "interrupted") {
|
|
3617
|
+
throw new Error(`Claude interrupted prompt history self-test failed: ${JSON.stringify(interrupted)}`);
|
|
3618
|
+
}
|
|
3616
3619
|
const taskUse = (uuid, id, name, input) => ({
|
|
3617
3620
|
type: "assistant",
|
|
3618
3621
|
uuid,
|
|
@@ -4434,14 +4437,14 @@ async function runSelfTest() {
|
|
|
4434
4437
|
.split("\n")
|
|
4435
4438
|
.filter(Boolean)
|
|
4436
4439
|
.map((line) => JSON.parse(line));
|
|
4437
|
-
const openingMessageIndex = openingEvents.findIndex((event) =>
|
|
4438
|
-
event.method === "item/completed"
|
|
4439
|
-
&& event.params?.item?.type === "agentMessage");
|
|
4440
|
-
const openingToolIndex = openingEvents.findIndex((event) =>
|
|
4441
|
-
event.method === "item/started" && event.params?.item?.type === "dynamicToolCall");
|
|
4442
|
-
if (openingMessageIndex >= 0 || openingToolIndex < 0) {
|
|
4443
|
-
throw new Error(`Claude tool-first turn self-test failed: ${JSON.stringify(openingEvents)}`);
|
|
4444
|
-
}
|
|
4440
|
+
const openingMessageIndex = openingEvents.findIndex((event) =>
|
|
4441
|
+
event.method === "item/completed"
|
|
4442
|
+
&& event.params?.item?.type === "agentMessage");
|
|
4443
|
+
const openingToolIndex = openingEvents.findIndex((event) =>
|
|
4444
|
+
event.method === "item/started" && event.params?.item?.type === "dynamicToolCall");
|
|
4445
|
+
if (openingMessageIndex >= 0 || openingToolIndex < 0) {
|
|
4446
|
+
throw new Error(`Claude tool-first turn self-test failed: ${JSON.stringify(openingEvents)}`);
|
|
4447
|
+
}
|
|
4445
4448
|
const languageCaptured = [];
|
|
4446
4449
|
process.stdout.write = (chunk) => {
|
|
4447
4450
|
languageCaptured.push(String(chunk));
|
package/install-skills.mjs
CHANGED
|
@@ -10,14 +10,14 @@ import { dirname, join } from "node:path";
|
|
|
10
10
|
import { fileURLToPath } from "node:url";
|
|
11
11
|
|
|
12
12
|
const packageRoot = dirname(fileURLToPath(import.meta.url));
|
|
13
|
-
const
|
|
13
|
+
const skillNames = ["luna-loop", "insane-search"];
|
|
14
14
|
const userHome = homedir();
|
|
15
15
|
const codexHome = process.env.CODEX_HOME?.trim() || join(userHome, ".codex");
|
|
16
16
|
const claudeHome = process.env.CLAUDE_CONFIG_DIR?.trim() || join(userHome, ".claude");
|
|
17
17
|
|
|
18
|
-
const
|
|
19
|
-
{ name: "Codex",
|
|
20
|
-
{ name: "Claude",
|
|
18
|
+
const homes = [
|
|
19
|
+
{ name: "Codex", root: join(codexHome, "skills") },
|
|
20
|
+
{ name: "Claude", root: join(claudeHome, "skills") },
|
|
21
21
|
];
|
|
22
22
|
|
|
23
23
|
function copyTree(source, destination) {
|
|
@@ -47,20 +47,25 @@ function pruneTree(source, destination) {
|
|
|
47
47
|
}
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
if (!existsSync(join(sourceRoot, "SKILL.md"))) {
|
|
51
|
-
console.error(`스킬 원본(luna-loop)을 찾지 못했습니다: ${sourceRoot}`);
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
50
|
let failed = false;
|
|
56
|
-
for (const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
console.log(`스킬 설치 완료 (${target.name}): ${target.path}`);
|
|
61
|
-
} catch (error) {
|
|
51
|
+
for (const skill of skillNames) {
|
|
52
|
+
const sourceRoot = join(packageRoot, "skills", skill);
|
|
53
|
+
// 원본이 없으면 그 스킬만 건너뛴다. 하나가 빠졌다고 나머지 설치까지 막지 않는다.
|
|
54
|
+
if (!existsSync(join(sourceRoot, "SKILL.md"))) {
|
|
62
55
|
failed = true;
|
|
63
|
-
console.error(`스킬
|
|
56
|
+
console.error(`스킬 원본(${skill})을 찾지 못했습니다: ${sourceRoot}`);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
for (const home of homes) {
|
|
60
|
+
const target = join(home.root, skill);
|
|
61
|
+
try {
|
|
62
|
+
copyTree(sourceRoot, target);
|
|
63
|
+
pruneTree(sourceRoot, target);
|
|
64
|
+
console.log(`스킬 설치 완료 (${home.name}/${skill}): ${target}`);
|
|
65
|
+
} catch (error) {
|
|
66
|
+
failed = true;
|
|
67
|
+
console.error(`스킬 설치 실패 (${home.name}/${skill}): ${error instanceof Error ? error.message : error}`);
|
|
68
|
+
}
|
|
64
69
|
}
|
|
65
70
|
}
|
|
66
71
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "devez-vibe",
|
|
3
|
-
"version": "1.7.
|
|
3
|
+
"version": "1.7.52",
|
|
4
4
|
"description": "Stable terminal UI for Codex and Claude Agent SDK",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"codex",
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
"bin/dvz.exe",
|
|
25
25
|
"bridge/claude-agent-sdk-bridge.mjs",
|
|
26
26
|
"install-skills.mjs",
|
|
27
|
+
"prepare-search-deps.mjs",
|
|
27
28
|
"skills/luna-loop/",
|
|
29
|
+
"skills/insane-search/",
|
|
28
30
|
"README.md",
|
|
29
31
|
"LICENSE"
|
|
30
32
|
],
|
|
@@ -38,7 +40,7 @@
|
|
|
38
40
|
"node": ">=18"
|
|
39
41
|
},
|
|
40
42
|
"scripts": {
|
|
41
|
-
"postinstall": "node install-skills.mjs"
|
|
43
|
+
"postinstall": "node install-skills.mjs && node prepare-search-deps.mjs"
|
|
42
44
|
},
|
|
43
45
|
"dependencies": {
|
|
44
46
|
"@anthropic-ai/claude-agent-sdk": "0.3.260"
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
// insane-search 스킬이 첫 호출에서 받는 파이썬 패키지를 설치 시점에 미리 깔아 둔다.
|
|
2
|
+
// 준비는 어디까지나 선택적이다. 파이썬이 없거나 설치가 실패해도 안내만 남기고 정상 종료해서
|
|
3
|
+
// devezVibe 설치 자체가 깨지지 않게 한다. 스킬은 첫 사용 때 다시 설치를 시도하므로 손해가 없다.
|
|
4
|
+
import { spawnSync } from "node:child_process";
|
|
5
|
+
|
|
6
|
+
// SKILL.md의 의존성 가드와 같은 조합이다. 한쪽만 바뀌면 첫 호출에서 다시 설치가 돈다.
|
|
7
|
+
const guard = 'import curl_cffi,bs4,yaml,pypdf,markdownify; v=curl_cffi.__version__.split("."); assert (int(v[0]),int(v[1]))>=(0,15)';
|
|
8
|
+
const packages = ["curl_cffi>=0.15.0", "beautifulsoup4", "pyyaml", "pypdf", "markdownify"];
|
|
9
|
+
|
|
10
|
+
function run(command, args) {
|
|
11
|
+
return spawnSync(command, args, { encoding: "utf8", windowsHide: true, timeout: 300000 });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Windows에는 python3가 없는 대신 실행하면 스토어를 여는 별칭이 잡혀 있을 수 있다.
|
|
15
|
+
// 버전 출력을 실제로 확인해서 쓸 수 있는 실행기만 고른다.
|
|
16
|
+
function findPython() {
|
|
17
|
+
for (const candidate of ["python3", "python", "py"]) {
|
|
18
|
+
const probe = run(candidate, ["-c", "import sys; print(sys.version_info[0])"]);
|
|
19
|
+
if (probe.status === 0 && probe.stdout.trim() === "3") return candidate;
|
|
20
|
+
}
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const python = findPython();
|
|
25
|
+
if (!python) {
|
|
26
|
+
console.log("insane-search 준비 건너뜀: 파이썬 3을 찾지 못했습니다. 설치하면 첫 사용 때 자동으로 준비됩니다.");
|
|
27
|
+
process.exit(0);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (run(python, ["-c", guard]).status === 0) {
|
|
31
|
+
console.log("insane-search 의존성 준비 완료: 이미 설치되어 있습니다.");
|
|
32
|
+
process.exit(0);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// --user는 관리자 권한 없이도 설치되게 하고, 가상환경에서는 pip가 알아서 무시한다.
|
|
36
|
+
const install = run(python, ["-m", "pip", "install", "-U", "--user", "-q", ...packages]);
|
|
37
|
+
if (install.status === 0 && run(python, ["-c", guard]).status === 0) {
|
|
38
|
+
console.log("insane-search 의존성 준비 완료");
|
|
39
|
+
} else {
|
|
40
|
+
const detail = (install.stderr || install.stdout || "").trim().split("\n").pop() || "원인 미상";
|
|
41
|
+
console.log(`insane-search 준비 건너뜀 (${detail}). 첫 사용 때 다시 시도합니다.`);
|
|
42
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 fivetaku
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|