calvyn-code 0.14.0 → 0.14.1
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/bin/calvyn.js +24 -1
- package/calvyn_constants.py +62 -43
- package/cli-config.yaml +21 -0
- package/cli.py +12 -6
- package/hermes_bootstrap.py +1 -0
- package/hermes_cli/__init__.py +1 -1
- package/hermes_cli/auth.py +3 -2
- package/hermes_cli/models.py +7 -3
- package/hermes_cli/providers.py +19 -9
- package/hermes_constants.py +1 -0
- package/hermes_logging.py +1 -0
- package/hermes_state.py +1 -0
- package/hermes_time.py +1 -0
- package/package.json +12 -2
- package/plugins/model-providers/freemodel/__init__.py +16 -0
- package/plugins/model-providers/freemodel/plugin.yaml +5 -0
- package/pyproject.toml +24 -26
- package/scripts/postinstall.js +20 -0
package/bin/calvyn.js
CHANGED
|
@@ -49,14 +49,37 @@ function runPostinstallIfNeeded() {
|
|
|
49
49
|
return calvynBinary
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
function shouldShowBanner(args) {
|
|
53
|
+
if (!args.length) return true
|
|
54
|
+
const first = String(args[0] || "").trim().toLowerCase()
|
|
55
|
+
return !["-h", "--help", "-v", "--version", "version"].includes(first)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function printNpmLaunchHeader() {
|
|
59
|
+
console.log("")
|
|
60
|
+
console.log("")
|
|
61
|
+
console.log("")
|
|
62
|
+
console.log(" ╔════════════════════════════════════════════════════════════════════════════╗")
|
|
63
|
+
console.log(" ║ ✦ CALVYN CODE ║")
|
|
64
|
+
console.log(" ║ Запуск через npm ║")
|
|
65
|
+
console.log(" ╚════════════════════════════════════════════════════════════════════════════╝")
|
|
66
|
+
console.log("")
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const args = process.argv.slice(2)
|
|
70
|
+
if (shouldShowBanner(args)) {
|
|
71
|
+
printNpmLaunchHeader()
|
|
72
|
+
}
|
|
73
|
+
|
|
52
74
|
const calvynBinary = runPostinstallIfNeeded()
|
|
53
|
-
const result = spawnSync(calvynBinary,
|
|
75
|
+
const result = spawnSync(calvynBinary, args, {
|
|
54
76
|
stdio: "inherit",
|
|
55
77
|
shell: false,
|
|
56
78
|
env: {
|
|
57
79
|
...process.env,
|
|
58
80
|
CALVYN_LAUNCHED_FROM_NPM: "1",
|
|
59
81
|
CALVYN_REPO_ROOT: packageRoot,
|
|
82
|
+
CALVYN_HOME: process.env.CALVYN_HOME || path.join(require("os").homedir(), ".calvyn"),
|
|
60
83
|
},
|
|
61
84
|
})
|
|
62
85
|
|
package/calvyn_constants.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Shared constants for
|
|
1
|
+
"""Shared constants for Calvyn Code.
|
|
2
2
|
|
|
3
3
|
Import-safe module with no dependencies — can be imported from anywhere
|
|
4
4
|
without risk of circular imports.
|
|
@@ -8,14 +8,24 @@ import os
|
|
|
8
8
|
from pathlib import Path
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
_profile_fallback_warned: bool = False
|
|
11
|
+
_profile_fallback_warned: bool = False
|
|
12
|
+
_CALVYN_HOME_ENV = "CALVYN_HOME"
|
|
13
|
+
_LEGACY_HOME_ENV = "HERMES_HOME"
|
|
14
|
+
_DEFAULT_HOME_DIRNAME = ".calvyn"
|
|
15
|
+
_LEGACY_HOME_DIRNAME = ".hermes"
|
|
12
16
|
|
|
13
17
|
|
|
14
|
-
def get_hermes_home() -> Path:
|
|
15
|
-
"""Return the
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
18
|
+
def get_hermes_home() -> Path:
|
|
19
|
+
"""Return the Calvyn home directory.
|
|
20
|
+
|
|
21
|
+
Preference order:
|
|
22
|
+
1. ``CALVYN_HOME``
|
|
23
|
+
2. ``HERMES_HOME`` for legacy compatibility
|
|
24
|
+
3. ``~/.calvyn``
|
|
25
|
+
4. ``~/.hermes`` only when it already exists and ``~/.calvyn`` does not
|
|
26
|
+
|
|
27
|
+
This keeps Calvyn isolated from Hermes by default while still allowing
|
|
28
|
+
explicit legacy overrides.
|
|
19
29
|
|
|
20
30
|
When ``HERMES_HOME`` is unset but an ``active_profile`` file indicates
|
|
21
31
|
a non-default profile is active, logs a loud one-shot warning to
|
|
@@ -27,22 +37,26 @@ def get_hermes_home() -> Path:
|
|
|
27
37
|
template in ``hermes_cli/gateway.py`` and the kanban dispatcher in
|
|
28
38
|
``hermes_cli/kanban_db.py``). See https://github.com/NousResearch/hermes-agent/issues/18594.
|
|
29
39
|
"""
|
|
30
|
-
val = os.environ.get(
|
|
31
|
-
if val:
|
|
32
|
-
return Path(val)
|
|
40
|
+
val = os.environ.get(_CALVYN_HOME_ENV, "").strip()
|
|
41
|
+
if val:
|
|
42
|
+
return Path(val)
|
|
43
|
+
|
|
44
|
+
val = os.environ.get(_LEGACY_HOME_ENV, "").strip()
|
|
45
|
+
if val:
|
|
46
|
+
return Path(val)
|
|
33
47
|
|
|
34
48
|
# Guard: if a non-default profile is sticky-active, warn once that
|
|
35
49
|
# the fallback to the default profile is almost certainly wrong.
|
|
36
50
|
global _profile_fallback_warned
|
|
37
51
|
if not _profile_fallback_warned:
|
|
38
52
|
try:
|
|
39
|
-
# Inline the default-root resolution from get_default_hermes_root()
|
|
40
|
-
# to stay import-safe (this function is called from module scope
|
|
41
|
-
# in 30+ files; we cannot afford to trigger logging setup here).
|
|
42
|
-
active_path = (Path.home() /
|
|
43
|
-
active = active_path.read_text().strip() if active_path.exists() else ""
|
|
44
|
-
except (UnicodeDecodeError, OSError):
|
|
45
|
-
active = ""
|
|
53
|
+
# Inline the default-root resolution from get_default_hermes_root()
|
|
54
|
+
# to stay import-safe (this function is called from module scope
|
|
55
|
+
# in 30+ files; we cannot afford to trigger logging setup here).
|
|
56
|
+
active_path = (Path.home() / _DEFAULT_HOME_DIRNAME / "active_profile")
|
|
57
|
+
active = active_path.read_text().strip() if active_path.exists() else ""
|
|
58
|
+
except (UnicodeDecodeError, OSError):
|
|
59
|
+
active = ""
|
|
46
60
|
if active and active != "default":
|
|
47
61
|
_profile_fallback_warned = True
|
|
48
62
|
# Write directly to stderr. We intentionally do NOT route this
|
|
@@ -52,24 +66,28 @@ def get_hermes_home() -> Path:
|
|
|
52
66
|
# on consoles where a StreamHandler is already attached.
|
|
53
67
|
import sys
|
|
54
68
|
msg = (
|
|
55
|
-
f"[
|
|
56
|
-
f"profile is {active!r}. Falling back to
|
|
57
|
-
f"is the DEFAULT profile — not {active!r}. Any data this "
|
|
58
|
-
f"process writes will land in the wrong profile. The "
|
|
59
|
-
f"subprocess spawner should pass
|
|
60
|
-
f"(see issue #18594)."
|
|
61
|
-
)
|
|
69
|
+
f"[CALVYN_HOME fallback] CALVYN_HOME is unset but active "
|
|
70
|
+
f"profile is {active!r}. Falling back to ~/{_DEFAULT_HOME_DIRNAME}, which "
|
|
71
|
+
f"is the DEFAULT profile — not {active!r}. Any data this "
|
|
72
|
+
f"process writes will land in the wrong profile. The "
|
|
73
|
+
f"subprocess spawner should pass CALVYN_HOME explicitly "
|
|
74
|
+
f"(see issue #18594)."
|
|
75
|
+
)
|
|
62
76
|
try:
|
|
63
77
|
sys.stderr.write(msg + "\n")
|
|
64
78
|
sys.stderr.flush()
|
|
65
79
|
except Exception:
|
|
66
80
|
pass
|
|
67
81
|
|
|
68
|
-
|
|
82
|
+
calvyn_home = Path.home() / _DEFAULT_HOME_DIRNAME
|
|
83
|
+
legacy_home = Path.home() / _LEGACY_HOME_DIRNAME
|
|
84
|
+
if calvyn_home.exists() or not legacy_home.exists():
|
|
85
|
+
return calvyn_home
|
|
86
|
+
return legacy_home
|
|
69
87
|
|
|
70
88
|
|
|
71
|
-
def get_default_hermes_root() -> Path:
|
|
72
|
-
"""Return the root
|
|
89
|
+
def get_default_hermes_root() -> Path:
|
|
90
|
+
"""Return the root Calvyn directory for profile-level operations.
|
|
73
91
|
|
|
74
92
|
In standard deployments this is ``~/.hermes``.
|
|
75
93
|
|
|
@@ -84,8 +102,8 @@ def get_default_hermes_root() -> Path:
|
|
|
84
102
|
|
|
85
103
|
Import-safe — no dependencies beyond stdlib.
|
|
86
104
|
"""
|
|
87
|
-
native_home = Path.home() /
|
|
88
|
-
env_home = os.environ.get("
|
|
105
|
+
native_home = Path.home() / _DEFAULT_HOME_DIRNAME
|
|
106
|
+
env_home = os.environ.get(_CALVYN_HOME_ENV, "") or os.environ.get(_LEGACY_HOME_ENV, "")
|
|
89
107
|
if not env_home:
|
|
90
108
|
return native_home
|
|
91
109
|
env_path = Path(env_home)
|
|
@@ -110,10 +128,11 @@ def get_default_hermes_root() -> Path:
|
|
|
110
128
|
def get_optional_skills_dir(default: Path | None = None) -> Path:
|
|
111
129
|
"""Return the optional-skills directory, honoring package-manager wrappers.
|
|
112
130
|
|
|
113
|
-
Packaged installs may ship ``optional-skills`` outside the Python package
|
|
114
|
-
tree and expose it via ``
|
|
115
|
-
|
|
116
|
-
|
|
131
|
+
Packaged installs may ship ``optional-skills`` outside the Python package
|
|
132
|
+
tree and expose it via ``CALVYN_OPTIONAL_SKILLS`` or the legacy
|
|
133
|
+
``HERMES_OPTIONAL_SKILLS``.
|
|
134
|
+
"""
|
|
135
|
+
override = os.getenv("CALVYN_OPTIONAL_SKILLS", "").strip() or os.getenv("HERMES_OPTIONAL_SKILLS", "").strip()
|
|
117
136
|
if override:
|
|
118
137
|
return Path(override)
|
|
119
138
|
if default is not None:
|
|
@@ -122,7 +141,7 @@ def get_optional_skills_dir(default: Path | None = None) -> Path:
|
|
|
122
141
|
|
|
123
142
|
|
|
124
143
|
def get_hermes_dir(new_subpath: str, old_name: str) -> Path:
|
|
125
|
-
"""Resolve a
|
|
144
|
+
"""Resolve a Calvyn subdirectory with backward compatibility.
|
|
126
145
|
|
|
127
146
|
New installs get the consolidated layout (e.g. ``cache/images``).
|
|
128
147
|
Existing installs that already have the old path (e.g. ``image_cache``)
|
|
@@ -143,17 +162,17 @@ def get_hermes_dir(new_subpath: str, old_name: str) -> Path:
|
|
|
143
162
|
|
|
144
163
|
|
|
145
164
|
def display_hermes_home() -> str:
|
|
146
|
-
"""Return a user-friendly display string for the current
|
|
165
|
+
"""Return a user-friendly display string for the current Calvyn home.
|
|
147
166
|
|
|
148
167
|
Uses ``~/`` shorthand for readability::
|
|
149
168
|
|
|
150
|
-
default: ``~/.
|
|
151
|
-
profile: ``~/.
|
|
152
|
-
custom: ``/opt/hermes-custom``
|
|
169
|
+
default: ``~/.calvyn``
|
|
170
|
+
profile: ``~/.calvyn/profiles/coder``
|
|
171
|
+
custom: ``/opt/hermes-custom``
|
|
153
172
|
|
|
154
|
-
Use this in **user-facing** print/log messages instead of hardcoding
|
|
155
|
-
``~/.
|
|
156
|
-
:func:`get_hermes_home` instead.
|
|
173
|
+
Use this in **user-facing** print/log messages instead of hardcoding
|
|
174
|
+
``~/.calvyn``. For code that needs a real ``Path``, use
|
|
175
|
+
:func:`get_hermes_home` instead.
|
|
157
176
|
"""
|
|
158
177
|
home = get_hermes_home()
|
|
159
178
|
try:
|
|
@@ -165,7 +184,7 @@ def display_hermes_home() -> str:
|
|
|
165
184
|
def get_subprocess_home() -> str | None:
|
|
166
185
|
"""Return a per-profile HOME directory for subprocesses, or None.
|
|
167
186
|
|
|
168
|
-
When ``{
|
|
187
|
+
When ``{CALVYN_HOME}/home/`` exists on disk, subprocesses should use it
|
|
169
188
|
as ``HOME`` so system tools (git, ssh, gh, npm …) write their configs
|
|
170
189
|
inside the Hermes data directory instead of the OS-level ``/root`` or
|
|
171
190
|
``~/``. This provides:
|
|
@@ -179,7 +198,7 @@ def get_subprocess_home() -> str | None:
|
|
|
179
198
|
Activation is directory-based: if the ``home/`` subdirectory doesn't
|
|
180
199
|
exist, returns ``None`` and behavior is unchanged.
|
|
181
200
|
"""
|
|
182
|
-
hermes_home = os.getenv(
|
|
201
|
+
hermes_home = os.getenv(_CALVYN_HOME_ENV) or os.getenv(_LEGACY_HOME_ENV)
|
|
183
202
|
if not hermes_home:
|
|
184
203
|
return None
|
|
185
204
|
profile_home = os.path.join(hermes_home, "home")
|
package/cli-config.yaml
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
model:
|
|
2
|
+
provider: "freemodel"
|
|
3
|
+
default: "gpt-5.5"
|
|
4
|
+
base_url: "https://api.freemodel.dev"
|
|
5
|
+
api_mode: "codex_responses"
|
|
6
|
+
|
|
7
|
+
agent:
|
|
8
|
+
reasoning_effort: "xhigh"
|
|
9
|
+
|
|
10
|
+
providers:
|
|
11
|
+
freemodel:
|
|
12
|
+
name: "freemodel"
|
|
13
|
+
base_url: "https://api.freemodel.dev"
|
|
14
|
+
key_env: "FREEMODEL_API_KEY"
|
|
15
|
+
api_mode: "codex_responses"
|
|
16
|
+
model: "gpt-5.5"
|
|
17
|
+
|
|
18
|
+
model_provider: "freemodel"
|
|
19
|
+
model_reasoning_effort: "xhigh"
|
|
20
|
+
disable_response_storage: true
|
|
21
|
+
preferred_auth_method: "apikey"
|
package/cli.py
CHANGED
|
@@ -11542,12 +11542,18 @@ class HermesCLI:
|
|
|
11542
11542
|
# responses, and prompt all appear pinned to the bottom — empty
|
|
11543
11543
|
# space stays above, not below. This prints enough blank lines to
|
|
11544
11544
|
# scroll the cursor to the last row before any content is rendered.
|
|
11545
|
-
try:
|
|
11546
|
-
|
|
11547
|
-
|
|
11548
|
-
|
|
11549
|
-
|
|
11550
|
-
|
|
11545
|
+
try:
|
|
11546
|
+
if os.getenv("CALVYN_LAUNCHED_FROM_NPM", "").strip().lower() not in {
|
|
11547
|
+
"1",
|
|
11548
|
+
"true",
|
|
11549
|
+
"yes",
|
|
11550
|
+
"on",
|
|
11551
|
+
}:
|
|
11552
|
+
_term_lines = shutil.get_terminal_size().lines
|
|
11553
|
+
if _term_lines > 2:
|
|
11554
|
+
print("\n" * (_term_lines - 1), end="", flush=True)
|
|
11555
|
+
except Exception:
|
|
11556
|
+
pass
|
|
11551
11557
|
|
|
11552
11558
|
self.show_banner()
|
|
11553
11559
|
# Surface any active supply-chain security advisories right after the
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from calvyn_bootstrap import * # noqa: F401,F403
|
package/hermes_cli/__init__.py
CHANGED
package/hermes_cli/auth.py
CHANGED
|
@@ -1387,8 +1387,9 @@ def resolve_provider(
|
|
|
1387
1387
|
"kimi": "kimi-coding", "kimi-for-coding": "kimi-coding", "moonshot": "kimi-coding",
|
|
1388
1388
|
"kimi-cn": "kimi-coding-cn", "moonshot-cn": "kimi-coding-cn",
|
|
1389
1389
|
"step": "stepfun", "stepfun-coding-plan": "stepfun",
|
|
1390
|
-
"arcee-ai": "arcee", "arceeai": "arcee",
|
|
1391
|
-
"gmi-cloud": "gmi", "gmicloud": "gmi",
|
|
1390
|
+
"arcee-ai": "arcee", "arceeai": "arcee",
|
|
1391
|
+
"gmi-cloud": "gmi", "gmicloud": "gmi",
|
|
1392
|
+
"freemodel": "freemodel", "free-model": "freemodel", "free_model": "freemodel",
|
|
1392
1393
|
"minimax-china": "minimax-cn", "minimax_cn": "minimax-cn",
|
|
1393
1394
|
"minimax-portal": "minimax-oauth", "minimax-global": "minimax-oauth", "minimax_oauth": "minimax-oauth",
|
|
1394
1395
|
"alibaba_coding": "alibaba-coding-plan", "alibaba-coding": "alibaba-coding-plan",
|
package/hermes_cli/models.py
CHANGED
|
@@ -949,7 +949,8 @@ CANONICAL_PROVIDERS: list[ProviderEntry] = [
|
|
|
949
949
|
ProviderEntry("minimax-cn", "MiniMax (China)", "MiniMax China (domestic direct API)"),
|
|
950
950
|
ProviderEntry("ollama-cloud", "Ollama Cloud", "Ollama Cloud (cloud-hosted open models — ollama.com)"),
|
|
951
951
|
ProviderEntry("arcee", "Arcee AI", "Arcee AI (Trinity models — direct API)"),
|
|
952
|
-
ProviderEntry("gmi", "GMI Cloud", "GMI Cloud (multi-model direct API)"),
|
|
952
|
+
ProviderEntry("gmi", "GMI Cloud", "GMI Cloud (multi-model direct API)"),
|
|
953
|
+
ProviderEntry("freemodel", "FreeModel", "FreeModel (OpenAI-compatible direct API)"),
|
|
953
954
|
ProviderEntry("kilocode", "Kilo Code", "Kilo Code (Kilo Gateway API)"),
|
|
954
955
|
ProviderEntry("opencode-zen", "OpenCode Zen", "OpenCode Zen (35+ curated models, pay-as-you-go)"),
|
|
955
956
|
ProviderEntry("opencode-go", "OpenCode Go", "OpenCode Go (open models, $10/month subscription)"),
|
|
@@ -1005,8 +1006,11 @@ _PROVIDER_ALIASES = {
|
|
|
1005
1006
|
"stepfun-coding-plan": "stepfun",
|
|
1006
1007
|
"arcee-ai": "arcee",
|
|
1007
1008
|
"arceeai": "arcee",
|
|
1008
|
-
"gmi-cloud": "gmi",
|
|
1009
|
-
"gmicloud": "gmi",
|
|
1009
|
+
"gmi-cloud": "gmi",
|
|
1010
|
+
"gmicloud": "gmi",
|
|
1011
|
+
"freemodel": "freemodel",
|
|
1012
|
+
"free-model": "freemodel",
|
|
1013
|
+
"free_model": "freemodel",
|
|
1010
1014
|
"minimax-china": "minimax-cn",
|
|
1011
1015
|
"minimax_cn": "minimax-cn",
|
|
1012
1016
|
"minimax-portal": "minimax-oauth",
|
package/hermes_cli/providers.py
CHANGED
|
@@ -190,12 +190,18 @@ HERMES_OVERLAYS: Dict[str, HermesOverlay] = {
|
|
|
190
190
|
base_url_override="https://api.arcee.ai/api/v1",
|
|
191
191
|
base_url_env_var="ARCEE_BASE_URL",
|
|
192
192
|
),
|
|
193
|
-
"gmi": HermesOverlay(
|
|
194
|
-
transport="openai_chat",
|
|
195
|
-
extra_env_vars=("GMI_API_KEY",),
|
|
196
|
-
base_url_override="https://api.gmi-serving.com/v1",
|
|
197
|
-
base_url_env_var="GMI_BASE_URL",
|
|
198
|
-
),
|
|
193
|
+
"gmi": HermesOverlay(
|
|
194
|
+
transport="openai_chat",
|
|
195
|
+
extra_env_vars=("GMI_API_KEY",),
|
|
196
|
+
base_url_override="https://api.gmi-serving.com/v1",
|
|
197
|
+
base_url_env_var="GMI_BASE_URL",
|
|
198
|
+
),
|
|
199
|
+
"freemodel": HermesOverlay(
|
|
200
|
+
transport="openai_chat",
|
|
201
|
+
extra_env_vars=("FREEMODEL_API_KEY",),
|
|
202
|
+
base_url_override="https://api.freemodel.xyz/v1",
|
|
203
|
+
base_url_env_var="FREEMODEL_BASE_URL",
|
|
204
|
+
),
|
|
199
205
|
"ollama-cloud": HermesOverlay(
|
|
200
206
|
transport="openai_chat",
|
|
201
207
|
base_url_env_var="OLLAMA_BASE_URL",
|
|
@@ -349,8 +355,11 @@ ALIASES: Dict[str, str] = {
|
|
|
349
355
|
"arceeai": "arcee",
|
|
350
356
|
|
|
351
357
|
# gmi
|
|
352
|
-
"gmi-cloud": "gmi",
|
|
353
|
-
"gmicloud": "gmi",
|
|
358
|
+
"gmi-cloud": "gmi",
|
|
359
|
+
"gmicloud": "gmi",
|
|
360
|
+
"freemodel": "freemodel",
|
|
361
|
+
"free-model": "freemodel",
|
|
362
|
+
"free_model": "freemodel",
|
|
354
363
|
|
|
355
364
|
# Local server aliases → virtual "local" concept (resolved via user config)
|
|
356
365
|
"lmstudio": "lmstudio",
|
|
@@ -374,7 +383,8 @@ _LABEL_OVERRIDES: Dict[str, str] = {
|
|
|
374
383
|
"copilot-acp": "GitHub Copilot ACP",
|
|
375
384
|
"stepfun": "StepFun Step Plan",
|
|
376
385
|
"xiaomi": "Xiaomi MiMo",
|
|
377
|
-
"gmi": "GMI Cloud",
|
|
386
|
+
"gmi": "GMI Cloud",
|
|
387
|
+
"freemodel": "FreeModel",
|
|
378
388
|
"tencent-tokenhub": "Tencent TokenHub",
|
|
379
389
|
"lmstudio": "LM Studio",
|
|
380
390
|
"local": "Local endpoint",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from calvyn_constants import * # noqa: F401,F403
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from calvyn_logging import * # noqa: F401,F403
|
package/hermes_state.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from calvyn_state import * # noqa: F401,F403
|
package/hermes_time.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from calvyn_time import * # noqa: F401,F403
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "calvyn-code",
|
|
3
|
-
"version": "0.14.
|
|
3
|
+
"version": "0.14.1",
|
|
4
4
|
"description": "Calvyn Code — AI агент с инструментами, мессенджерами и локальным CLI",
|
|
5
5
|
"bin": {
|
|
6
|
-
"calvyn": "./bin/calvyn.js"
|
|
6
|
+
"calvyn": "./bin/calvyn.js",
|
|
7
|
+
"calvyn-code": "./bin/calvyn.js"
|
|
7
8
|
},
|
|
8
9
|
"main": "./bin/calvyn.js",
|
|
9
10
|
"keywords": [
|
|
@@ -40,6 +41,9 @@
|
|
|
40
41
|
"engines": {
|
|
41
42
|
"node": ">=20.0.0"
|
|
42
43
|
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public"
|
|
46
|
+
},
|
|
43
47
|
"files": [
|
|
44
48
|
"bin",
|
|
45
49
|
"scripts",
|
|
@@ -68,9 +72,15 @@
|
|
|
68
72
|
"calvyn_state.py",
|
|
69
73
|
"calvyn_time.py",
|
|
70
74
|
"calvyn_logging.py",
|
|
75
|
+
"hermes_bootstrap.py",
|
|
76
|
+
"hermes_constants.py",
|
|
77
|
+
"hermes_state.py",
|
|
78
|
+
"hermes_time.py",
|
|
79
|
+
"hermes_logging.py",
|
|
71
80
|
"mcp_serve.py",
|
|
72
81
|
"utils.py",
|
|
73
82
|
"pyproject.toml",
|
|
83
|
+
"cli-config.yaml",
|
|
74
84
|
"README.md",
|
|
75
85
|
"LICENSE"
|
|
76
86
|
]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""FreeModel provider profile."""
|
|
2
|
+
|
|
3
|
+
from providers import register_provider
|
|
4
|
+
from providers.base import ProviderProfile
|
|
5
|
+
|
|
6
|
+
freemodel = ProviderProfile(
|
|
7
|
+
name="freemodel",
|
|
8
|
+
aliases=("free-model", "free_model"),
|
|
9
|
+
display_name="FreeModel",
|
|
10
|
+
description="FreeModel — OpenAI-compatible direct API",
|
|
11
|
+
signup_url="https://freemodel.xyz/",
|
|
12
|
+
env_vars=("FREEMODEL_API_KEY", "FREEMODEL_BASE_URL"),
|
|
13
|
+
base_url="https://api.freemodel.xyz/v1",
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
register_provider(freemodel)
|
package/pyproject.toml
CHANGED
|
@@ -3,8 +3,8 @@ requires = ["setuptools>=61.0"]
|
|
|
3
3
|
build-backend = "setuptools.build_meta"
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
|
-
name = "
|
|
7
|
-
version = "0.14.
|
|
6
|
+
name = "calvyn-code"
|
|
7
|
+
version = "0.14.1"
|
|
8
8
|
description = "The self-improving AI agent — creates skills from experience, improves them during use, and runs anywhere"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -128,23 +128,23 @@ bedrock = ["boto3==1.42.89"]
|
|
|
128
128
|
termux = [
|
|
129
129
|
# Baseline Android / Termux path for reliable fresh installs.
|
|
130
130
|
"python-telegram-bot[webhooks]==22.6",
|
|
131
|
-
"
|
|
132
|
-
"
|
|
133
|
-
"
|
|
134
|
-
"
|
|
135
|
-
"
|
|
136
|
-
"
|
|
131
|
+
"calvyn-code[cron]",
|
|
132
|
+
"calvyn-code[cli]",
|
|
133
|
+
"calvyn-code[pty]",
|
|
134
|
+
"calvyn-code[mcp]",
|
|
135
|
+
"calvyn-code[honcho]",
|
|
136
|
+
"calvyn-code[acp]",
|
|
137
137
|
]
|
|
138
138
|
termux-all = [
|
|
139
139
|
# Best-effort "install all" profile for Termux. Same policy as [all]:
|
|
140
140
|
# only includes extras that aren't covered by `tools/lazy_deps.py`.
|
|
141
141
|
# Backends like telegram/slack/dingtalk/feishu/honcho lazy-install at
|
|
142
142
|
# first use, so they're no longer eager-installed here.
|
|
143
|
-
"
|
|
144
|
-
"
|
|
145
|
-
"
|
|
146
|
-
"
|
|
147
|
-
"
|
|
143
|
+
"calvyn-code[termux]",
|
|
144
|
+
"calvyn-code[google]",
|
|
145
|
+
"calvyn-code[homeassistant]",
|
|
146
|
+
"calvyn-code[sms]",
|
|
147
|
+
"calvyn-code[web]",
|
|
148
148
|
]
|
|
149
149
|
dingtalk = ["dingtalk-stream==0.24.3", "alibabacloud-dingtalk==2.2.42", "qrcode==7.4.2"]
|
|
150
150
|
feishu = ["lark-oapi==1.5.3", "qrcode==7.4.2"]
|
|
@@ -188,25 +188,23 @@ all = [
|
|
|
188
188
|
# [all], `uv sync --locked` on Windows tried to build it from sdist
|
|
189
189
|
# and failed on `make`. Lazy-install routes that build to first use,
|
|
190
190
|
# where the user is expected to have a toolchain available.
|
|
191
|
-
"
|
|
192
|
-
"
|
|
193
|
-
"
|
|
194
|
-
"
|
|
195
|
-
"
|
|
196
|
-
"
|
|
197
|
-
"
|
|
198
|
-
"
|
|
199
|
-
"
|
|
200
|
-
"
|
|
201
|
-
"
|
|
191
|
+
"calvyn-code[cron]",
|
|
192
|
+
"calvyn-code[cli]",
|
|
193
|
+
"calvyn-code[dev]",
|
|
194
|
+
"calvyn-code[pty]",
|
|
195
|
+
"calvyn-code[mcp]",
|
|
196
|
+
"calvyn-code[homeassistant]",
|
|
197
|
+
"calvyn-code[sms]",
|
|
198
|
+
"calvyn-code[acp]",
|
|
199
|
+
"calvyn-code[google]",
|
|
200
|
+
"calvyn-code[web]",
|
|
201
|
+
"calvyn-code[youtube]",
|
|
202
202
|
]
|
|
203
203
|
|
|
204
204
|
[project.scripts]
|
|
205
205
|
calvyn = "hermes_cli.main:main"
|
|
206
206
|
calvyn-agent = "run_agent:main"
|
|
207
207
|
calvyn-acp = "acp_adapter.entry:main"
|
|
208
|
-
hermes-agent = "run_agent:main"
|
|
209
|
-
hermes-acp = "acp_adapter.entry:main"
|
|
210
208
|
|
|
211
209
|
[tool.setuptools]
|
|
212
210
|
py-modules = ["run_agent", "model_tools", "toolsets", "batch_runner", "trajectory_compressor", "toolset_distributions", "cli", "calvyn_bootstrap", "calvyn_constants", "calvyn_state", "calvyn_time", "calvyn_logging", "utils"]
|
package/scripts/postinstall.js
CHANGED
|
@@ -95,6 +95,25 @@ function getVenvPaths() {
|
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
function ensureDefaultConfig() {
|
|
99
|
+
const homeDir = process.env.CALVYN_HOME
|
|
100
|
+
? path.resolve(process.env.CALVYN_HOME)
|
|
101
|
+
: path.join(require("os").homedir(), ".calvyn")
|
|
102
|
+
const configDir = homeDir
|
|
103
|
+
const configPath = path.join(configDir, "config.yaml")
|
|
104
|
+
const bundledConfig = path.join(packageRoot, "cli-config.yaml")
|
|
105
|
+
|
|
106
|
+
if (!fs.existsSync(bundledConfig)) {
|
|
107
|
+
return
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
fs.mkdirSync(configDir, { recursive: true })
|
|
111
|
+
if (!fs.existsSync(configPath)) {
|
|
112
|
+
fs.copyFileSync(bundledConfig, configPath)
|
|
113
|
+
log(`Положил стартовый конфиг в ${configPath}`)
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
98
117
|
function ensureVenv(python) {
|
|
99
118
|
const venv = getVenvPaths()
|
|
100
119
|
if (!fs.existsSync(venv.dir)) {
|
|
@@ -137,6 +156,7 @@ function main() {
|
|
|
137
156
|
}
|
|
138
157
|
|
|
139
158
|
ensureInstalled(venv.python)
|
|
159
|
+
ensureDefaultConfig()
|
|
140
160
|
|
|
141
161
|
if (!fs.existsSync(venv.calvyn)) {
|
|
142
162
|
fail("Установка прошла не до конца: команда calvyn внутри .venv не найдена")
|