@oneciel-ai/claude-any 0.1.83 → 0.1.84
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/claude_any.py +201 -44
- package/package.json +1 -1
package/claude_any.py
CHANGED
|
@@ -104,7 +104,7 @@ OFFICIAL_CHANNEL_PLUGINS = {
|
|
|
104
104
|
"fakechat": "plugin:fakechat@claude-plugins-official",
|
|
105
105
|
}
|
|
106
106
|
APP_NAME = "Claude Any"
|
|
107
|
-
VERSION = "0.1.
|
|
107
|
+
VERSION = "0.1.84"
|
|
108
108
|
CREDITS = "Credits: One Ciel LLC"
|
|
109
109
|
|
|
110
110
|
LOG_LEVELS = {"SILENT": 0, "ERROR": 1, "WARN": 2, "INFO": 3, "DEBUG": 4, "TRACE": 5}
|
|
@@ -1052,6 +1052,7 @@ UI_TEXT = {
|
|
|
1052
1052
|
"advisor_model": "Advisor Model",
|
|
1053
1053
|
"test": "Test compatibility",
|
|
1054
1054
|
"options": "LLM options",
|
|
1055
|
+
"channel_delivery": "Channel delivery",
|
|
1055
1056
|
"log_level": "Log level",
|
|
1056
1057
|
"presets": "LLM presets",
|
|
1057
1058
|
"context_setup": "Context setup",
|
|
@@ -1073,6 +1074,7 @@ UI_TEXT = {
|
|
|
1073
1074
|
"advisor_model": "Advisor Model",
|
|
1074
1075
|
"test": "호환성 테스트",
|
|
1075
1076
|
"options": "LLM 옵션",
|
|
1077
|
+
"channel_delivery": "채널 전달 방식",
|
|
1076
1078
|
"log_level": "로그 레벨",
|
|
1077
1079
|
"presets": "LLM 프리셋",
|
|
1078
1080
|
"context_setup": "컨텍스트 설정",
|
|
@@ -1094,6 +1096,7 @@ UI_TEXT = {
|
|
|
1094
1096
|
"advisor_model": "Advisor Model",
|
|
1095
1097
|
"test": "互換性テスト",
|
|
1096
1098
|
"options": "LLMオプション",
|
|
1099
|
+
"channel_delivery": "チャンネル配信方式",
|
|
1097
1100
|
"log_level": "ログレベル",
|
|
1098
1101
|
"presets": "LLMプリセット",
|
|
1099
1102
|
"context_setup": "コンテキスト設定",
|
|
@@ -1115,6 +1118,7 @@ UI_TEXT = {
|
|
|
1115
1118
|
"advisor_model": "Advisor Model",
|
|
1116
1119
|
"test": "兼容性测试",
|
|
1117
1120
|
"options": "LLM 选项",
|
|
1121
|
+
"channel_delivery": "频道投递方式",
|
|
1118
1122
|
"log_level": "日志级别",
|
|
1119
1123
|
"presets": "LLM 预设",
|
|
1120
1124
|
"context_setup": "上下文设置",
|
|
@@ -1261,6 +1265,7 @@ DEFAULT_CONFIG: dict[str, Any] = {
|
|
|
1261
1265
|
"compat_prompt_for_non_anthropic": True,
|
|
1262
1266
|
"channels": [],
|
|
1263
1267
|
"development_channels": False,
|
|
1268
|
+
"channel_delivery": "stdin",
|
|
1264
1269
|
},
|
|
1265
1270
|
"cleanup": {
|
|
1266
1271
|
"managed_services_on_launch": True,
|
|
@@ -4836,27 +4841,63 @@ def _channel_mcp_session_id() -> str:
|
|
|
4836
4841
|
return f"s{os.getpid()}-{time.time_ns()}"
|
|
4837
4842
|
|
|
4838
4843
|
|
|
4844
|
+
def _native_channel_meta_value(value: Any) -> str:
|
|
4845
|
+
if value is None:
|
|
4846
|
+
return ""
|
|
4847
|
+
if isinstance(value, str):
|
|
4848
|
+
return value
|
|
4849
|
+
if isinstance(value, bool):
|
|
4850
|
+
return "true" if value else "false"
|
|
4851
|
+
if isinstance(value, (int, float)):
|
|
4852
|
+
return str(value)
|
|
4853
|
+
return json.dumps(_json_safe_metadata(value), ensure_ascii=False, separators=(",", ":"), default=str)
|
|
4854
|
+
|
|
4855
|
+
|
|
4856
|
+
def _native_channel_meta(message: dict[str, Any]) -> dict[str, str]:
|
|
4857
|
+
raw_meta = message.get("meta") if isinstance(message.get("meta"), dict) else {}
|
|
4858
|
+
meta: dict[str, str] = {}
|
|
4859
|
+
for key, value in raw_meta.items():
|
|
4860
|
+
name = str(key or "").strip()
|
|
4861
|
+
if not name:
|
|
4862
|
+
continue
|
|
4863
|
+
meta[name] = _native_channel_meta_value(value)
|
|
4864
|
+
base = {
|
|
4865
|
+
"claude_any_message_id": message.get("id"),
|
|
4866
|
+
"channel": message.get("channel") or "default",
|
|
4867
|
+
"sender_id": message.get("sender_id") or "channel",
|
|
4868
|
+
"thread_id": message.get("thread_id"),
|
|
4869
|
+
"parent_id": message.get("parent_id"),
|
|
4870
|
+
"kind": message.get("kind"),
|
|
4871
|
+
}
|
|
4872
|
+
for key, value in base.items():
|
|
4873
|
+
if value is not None:
|
|
4874
|
+
meta[key] = _native_channel_meta_value(value)
|
|
4875
|
+
if raw_meta:
|
|
4876
|
+
meta["claude_any_meta_json"] = json.dumps(_json_safe_metadata(raw_meta), ensure_ascii=False, separators=(",", ":"), default=str)
|
|
4877
|
+
return meta
|
|
4878
|
+
|
|
4879
|
+
|
|
4839
4880
|
def _channel_mcp_notification(message: dict[str, Any]) -> dict[str, Any]:
|
|
4840
4881
|
text = re.sub(r"\s+", " ", str(message.get("message") or "")).strip()
|
|
4841
4882
|
channel = str(message.get("channel") or "default")
|
|
4842
4883
|
sender = str(message.get("sender_id") or "channel")
|
|
4843
4884
|
prefix = f"[{channel}] {sender}"
|
|
4844
4885
|
content = f"{prefix}: {text}" if text else prefix
|
|
4845
|
-
meta = message.get("meta") if isinstance(message.get("meta"), dict) else {}
|
|
4846
|
-
merged_meta = {
|
|
4847
|
-
**meta,
|
|
4848
|
-
"claude_any_message_id": message.get("id"),
|
|
4849
|
-
"channel": channel,
|
|
4850
|
-
"sender_id": sender,
|
|
4851
|
-
"thread_id": message.get("thread_id"),
|
|
4852
|
-
"parent_id": message.get("parent_id"),
|
|
4853
|
-
}
|
|
4854
4886
|
return {
|
|
4855
4887
|
"jsonrpc": "2.0",
|
|
4856
4888
|
"method": "notifications/claude/channel",
|
|
4857
4889
|
"params": {
|
|
4858
4890
|
"content": content,
|
|
4859
|
-
"meta":
|
|
4891
|
+
"meta": _native_channel_meta(message),
|
|
4892
|
+
},
|
|
4893
|
+
}
|
|
4894
|
+
|
|
4895
|
+
|
|
4896
|
+
def _channel_mcp_capabilities() -> dict[str, Any]:
|
|
4897
|
+
return {
|
|
4898
|
+
"tools": {"listChanged": False},
|
|
4899
|
+
"experimental": {
|
|
4900
|
+
"claude/channel": {},
|
|
4860
4901
|
},
|
|
4861
4902
|
}
|
|
4862
4903
|
|
|
@@ -4941,7 +4982,7 @@ def handle_channel_mcp_post(handler: BaseHTTPRequestHandler, path: str, body: di
|
|
|
4941
4982
|
"id": request_id,
|
|
4942
4983
|
"result": {
|
|
4943
4984
|
"protocolVersion": protocol,
|
|
4944
|
-
"capabilities":
|
|
4985
|
+
"capabilities": _channel_mcp_capabilities(),
|
|
4945
4986
|
"serverInfo": {"name": "claude-any-router", "version": VERSION},
|
|
4946
4987
|
},
|
|
4947
4988
|
},
|
|
@@ -8949,6 +8990,7 @@ def status_lines() -> list[str]:
|
|
|
8949
8990
|
f"claude_model: {current_upstream_model_id(provider, pcfg) if direct_native else current_alias(cfg)}",
|
|
8950
8991
|
f"log_level: {log_level_status()}",
|
|
8951
8992
|
f"channels: {channel_status_text(cfg)}",
|
|
8993
|
+
f"channel_delivery: {channel_delivery_mode(cfg)}",
|
|
8952
8994
|
f"router: {'bypassed for native provider compatibility' if direct_native else (('up' if router_up() else 'down') + ' ' + ROUTER_BASE)}",
|
|
8953
8995
|
f"config: {CONFIG_PATH}",
|
|
8954
8996
|
]
|
|
@@ -9384,6 +9426,33 @@ def set_channel_development_enabled(enabled: bool) -> list[str]:
|
|
|
9384
9426
|
return ["Channel wake delivery is always enabled by Claude Any."]
|
|
9385
9427
|
|
|
9386
9428
|
|
|
9429
|
+
def normalize_channel_delivery(value: Any) -> str:
|
|
9430
|
+
text = str(value or "").strip().lower().replace("_", "-")
|
|
9431
|
+
if text in {"native", "native-channel", "native-channel-bridge", "claude-channel", "claude/native"}:
|
|
9432
|
+
return "native"
|
|
9433
|
+
if text in {"stdin", "pty", "terminal", "wake", "wake-proxy", "legacy", "auto", ""}:
|
|
9434
|
+
return "stdin"
|
|
9435
|
+
return "stdin"
|
|
9436
|
+
|
|
9437
|
+
|
|
9438
|
+
def channel_delivery_mode(cfg: dict[str, Any] | None = None) -> str:
|
|
9439
|
+
env_value = os.environ.get("CLAUDE_ANY_CHANNEL_DELIVERY")
|
|
9440
|
+
if env_value is not None:
|
|
9441
|
+
return normalize_channel_delivery(env_value)
|
|
9442
|
+
cfg = cfg or load_config()
|
|
9443
|
+
return normalize_channel_delivery(cfg.setdefault("claude_code", {}).get("channel_delivery", "stdin"))
|
|
9444
|
+
|
|
9445
|
+
|
|
9446
|
+
def set_channel_delivery_config(value: Any) -> list[str]:
|
|
9447
|
+
mode = normalize_channel_delivery(value)
|
|
9448
|
+
cfg = load_config()
|
|
9449
|
+
cfg.setdefault("claude_code", {})["channel_delivery"] = mode
|
|
9450
|
+
save_config(cfg)
|
|
9451
|
+
if mode == "native":
|
|
9452
|
+
return ["Channel delivery set to native claude/channel bridge."]
|
|
9453
|
+
return ["Channel delivery set to stdin wake proxy."]
|
|
9454
|
+
|
|
9455
|
+
|
|
9387
9456
|
def add_channel_spec(spec: str, *, development: bool = False) -> list[str]:
|
|
9388
9457
|
spec = spec.strip()
|
|
9389
9458
|
if not spec:
|
|
@@ -9422,6 +9491,7 @@ def cmd_channels(args: argparse.Namespace) -> None:
|
|
|
9422
9491
|
values = list(getattr(args, "values", []) or [])
|
|
9423
9492
|
if not values:
|
|
9424
9493
|
print(f"channels: {channel_status_text(cfg)}")
|
|
9494
|
+
print(f"delivery: {channel_delivery_mode(cfg)}")
|
|
9425
9495
|
for name, spec in OFFICIAL_CHANNEL_PLUGINS.items():
|
|
9426
9496
|
mark = "*" if spec in channel_specs(cfg) else " "
|
|
9427
9497
|
print(f" {mark} {name:<10} {spec}")
|
|
@@ -9456,6 +9526,13 @@ def cmd_channels(args: argparse.Namespace) -> None:
|
|
|
9456
9526
|
for line in clear_channel_specs():
|
|
9457
9527
|
print(line)
|
|
9458
9528
|
return
|
|
9529
|
+
if head in ("delivery", "mode"):
|
|
9530
|
+
if len(values) < 2:
|
|
9531
|
+
print(f"channel_delivery: {channel_delivery_mode(cfg)}")
|
|
9532
|
+
return
|
|
9533
|
+
for line in set_channel_delivery_config(values[1]):
|
|
9534
|
+
print(line)
|
|
9535
|
+
return
|
|
9459
9536
|
if head in OFFICIAL_CHANNEL_PLUGINS:
|
|
9460
9537
|
spec = OFFICIAL_CHANNEL_PLUGINS[head]
|
|
9461
9538
|
if spec in channel_specs(cfg):
|
|
@@ -9469,6 +9546,15 @@ def cmd_channels(args: argparse.Namespace) -> None:
|
|
|
9469
9546
|
print(line)
|
|
9470
9547
|
|
|
9471
9548
|
|
|
9549
|
+
def cmd_channel_delivery(args: argparse.Namespace) -> None:
|
|
9550
|
+
value = getattr(args, "value", None)
|
|
9551
|
+
if value:
|
|
9552
|
+
for line in set_channel_delivery_config(value):
|
|
9553
|
+
print(line)
|
|
9554
|
+
else:
|
|
9555
|
+
print(f"channel_delivery: {channel_delivery_mode()}")
|
|
9556
|
+
|
|
9557
|
+
|
|
9472
9558
|
def cmd_ollama_native(args: argparse.Namespace) -> None:
|
|
9473
9559
|
cfg = load_config()
|
|
9474
9560
|
pcfg = cfg["providers"]["ollama"]
|
|
@@ -12646,18 +12732,19 @@ def compact_text(value: Any, width: int = 72) -> str:
|
|
|
12646
12732
|
return fit_cells(value, width)
|
|
12647
12733
|
|
|
12648
12734
|
|
|
12649
|
-
def main_menu_rows(cfg: dict[str, Any], provider: str, pcfg: dict[str, Any], lang: str) -> list[str]:
|
|
12650
|
-
return [
|
|
12651
|
-
f"0. {ui_text('language', lang)} [{LANGUAGES.get(lang, lang)}]",
|
|
12652
|
-
f"1. {ui_text('provider', lang)} [{provider}]",
|
|
12653
|
-
f"2. {ui_text('api_key', lang)} [{stored_api_key_mask(provider, pcfg)}]",
|
|
12654
|
-
f"3. {ui_text('base_url', lang)} [{compact_text(pcfg.get('base_url', 'unset'), 62)}]",
|
|
12735
|
+
def main_menu_rows(cfg: dict[str, Any], provider: str, pcfg: dict[str, Any], lang: str) -> list[str]:
|
|
12736
|
+
return [
|
|
12737
|
+
f"0. {ui_text('language', lang)} [{LANGUAGES.get(lang, lang)}]",
|
|
12738
|
+
f"1. {ui_text('provider', lang)} [{provider}]",
|
|
12739
|
+
f"2. {ui_text('api_key', lang)} [{stored_api_key_mask(provider, pcfg)}]",
|
|
12740
|
+
f"3. {ui_text('base_url', lang)} [{compact_text(pcfg.get('base_url', 'unset'), 62)}]",
|
|
12655
12741
|
f"4. {ui_text('model', lang)} [{compact_text(pcfg.get('current_model', 'unset'), 62)}]",
|
|
12656
12742
|
f"5. {ui_text('advisor_model', lang)} [{compact_text(pcfg.get('advisor_model') or 'off', 62)}]",
|
|
12657
12743
|
f"6. {ui_text('options', lang)} [{compact_text(llm_options_status(provider, pcfg), 62)}]",
|
|
12658
|
-
f"7. {ui_text('
|
|
12659
|
-
f"8. {ui_text('
|
|
12660
|
-
f"9. {ui_text('
|
|
12744
|
+
f"7. {ui_text('channel_delivery', lang)} [{channel_delivery_mode(cfg)}]",
|
|
12745
|
+
f"8. {ui_text('log_level', lang)} [{log_level_status()}]",
|
|
12746
|
+
f"9. {ui_text('test', lang)}",
|
|
12747
|
+
f"10. {ui_text('launch', lang)}",
|
|
12661
12748
|
ui_text("quit", lang),
|
|
12662
12749
|
]
|
|
12663
12750
|
|
|
@@ -12773,6 +12860,16 @@ def channel_panel_rows(cfg: dict[str, Any]) -> tuple[list[str], list[str]]:
|
|
|
12773
12860
|
return rows, values
|
|
12774
12861
|
|
|
12775
12862
|
|
|
12863
|
+
def channel_delivery_panel_rows(cfg: dict[str, Any]) -> tuple[list[str], list[str]]:
|
|
12864
|
+
current = channel_delivery_mode(cfg)
|
|
12865
|
+
rows = [
|
|
12866
|
+
f"{'*' if current == 'stdin' else ' '} stdin PTY wake proxy; works broadly, uses terminal input",
|
|
12867
|
+
f"{'*' if current == 'native' else ' '} native Claude Code claude/channel queue bridge",
|
|
12868
|
+
"Back",
|
|
12869
|
+
]
|
|
12870
|
+
return rows, ["stdin", "native", "back"]
|
|
12871
|
+
|
|
12872
|
+
|
|
12776
12873
|
def api_key_panel_rows(provider: str) -> tuple[list[str], list[str]]:
|
|
12777
12874
|
rows = [
|
|
12778
12875
|
"Type or paste API key as hidden input",
|
|
@@ -12866,13 +12963,16 @@ def render_prelaunch_screen(
|
|
|
12866
12963
|
"api-key": "API key",
|
|
12867
12964
|
"base-url": "Base URL",
|
|
12868
12965
|
"model": "Model",
|
|
12869
|
-
"advisor-model": "Advisor Model",
|
|
12870
|
-
"test": "Compatibility test",
|
|
12871
|
-
"options": ui_text("options", lang),
|
|
12872
|
-
"
|
|
12873
|
-
"
|
|
12874
|
-
"
|
|
12875
|
-
|
|
12966
|
+
"advisor-model": "Advisor Model",
|
|
12967
|
+
"test": "Compatibility test",
|
|
12968
|
+
"options": ui_text("options", lang),
|
|
12969
|
+
"channel-delivery": ui_text("channel_delivery", lang),
|
|
12970
|
+
"log-level": ui_text("log_level", lang),
|
|
12971
|
+
"channels": "Channels",
|
|
12972
|
+
"context": ui_text("context_setup", lang),
|
|
12973
|
+
"preset": ui_text("presets", lang),
|
|
12974
|
+
"timeout": ui_text("timeout_preset", lang),
|
|
12975
|
+
}
|
|
12876
12976
|
add("")
|
|
12877
12977
|
add("-" * render_width, "38;5;208")
|
|
12878
12978
|
panel_title = titles.get(panel, panel)
|
|
@@ -13073,7 +13173,7 @@ def portable_language_menu() -> int:
|
|
|
13073
13173
|
|
|
13074
13174
|
def portable_prelaunch_menu() -> int:
|
|
13075
13175
|
enable_ansi()
|
|
13076
|
-
main_idx =
|
|
13176
|
+
main_idx = 10 if settings_ready_except_api_key() else 0
|
|
13077
13177
|
panel: str | None = None
|
|
13078
13178
|
panel_idx = 0
|
|
13079
13179
|
panel_rows: list[str] = []
|
|
@@ -13115,6 +13215,8 @@ def portable_prelaunch_menu() -> int:
|
|
|
13115
13215
|
panel_rows, panel_values = ["Run compatibility test", "Back"], ["run", "back"]
|
|
13116
13216
|
elif name == "options":
|
|
13117
13217
|
panel_rows, panel_values = llm_option_panel_rows(provider, pcfg, cfg.get("language", "en"))
|
|
13218
|
+
elif name == "channel-delivery":
|
|
13219
|
+
panel_rows, panel_values = channel_delivery_panel_rows(cfg)
|
|
13118
13220
|
elif name == "log-level":
|
|
13119
13221
|
panel_rows, panel_values = log_level_panel_rows(cfg)
|
|
13120
13222
|
elif name == "channels":
|
|
@@ -13295,7 +13397,7 @@ def portable_prelaunch_menu() -> int:
|
|
|
13295
13397
|
messages = lines[-8:] if lines else ["Test produced no output."]
|
|
13296
13398
|
panel_rows, panel_values = ["Run compatibility test again", "Back"], ["run", "back"]
|
|
13297
13399
|
refresh_checks()
|
|
13298
|
-
main_idx =
|
|
13400
|
+
main_idx = 10 if "Compatibility: OK" in out else 4
|
|
13299
13401
|
elif panel == "log-level":
|
|
13300
13402
|
if value == "back":
|
|
13301
13403
|
close_panel()
|
|
@@ -13305,6 +13407,15 @@ def portable_prelaunch_menu() -> int:
|
|
|
13305
13407
|
cfg = load_config()
|
|
13306
13408
|
panel_rows, panel_values = log_level_panel_rows(cfg)
|
|
13307
13409
|
panel_idx = max(0, min(panel_idx, len(panel_rows) - 1))
|
|
13410
|
+
elif panel == "channel-delivery":
|
|
13411
|
+
if value == "back":
|
|
13412
|
+
close_panel()
|
|
13413
|
+
elif value:
|
|
13414
|
+
messages = set_channel_delivery_config(value)
|
|
13415
|
+
refresh_checks()
|
|
13416
|
+
cfg = load_config()
|
|
13417
|
+
panel_rows, panel_values = channel_delivery_panel_rows(cfg)
|
|
13418
|
+
panel_idx = max(0, min(panel_idx, len(panel_rows) - 1))
|
|
13308
13419
|
elif panel == "channels":
|
|
13309
13420
|
if value == "back":
|
|
13310
13421
|
close_panel()
|
|
@@ -13431,14 +13542,18 @@ def portable_prelaunch_menu() -> int:
|
|
|
13431
13542
|
continue
|
|
13432
13543
|
|
|
13433
13544
|
if key in ("up", "k"):
|
|
13434
|
-
|
|
13545
|
+
cfg = load_config()
|
|
13546
|
+
provider, pcfg = get_current_provider(cfg)
|
|
13547
|
+
main_idx = (main_idx - 1) % len(main_menu_rows(cfg, provider, pcfg, cfg.get("language", "en")))
|
|
13435
13548
|
elif key in ("down", "j"):
|
|
13436
|
-
|
|
13549
|
+
cfg = load_config()
|
|
13550
|
+
provider, pcfg = get_current_provider(cfg)
|
|
13551
|
+
main_idx = (main_idx + 1) % len(main_menu_rows(cfg, provider, pcfg, cfg.get("language", "en")))
|
|
13437
13552
|
elif key in ("esc", "q"):
|
|
13438
13553
|
return 10
|
|
13439
13554
|
elif key == "enter":
|
|
13440
|
-
actions = ["language", "provider", "api-key", "base-url", "model", "advisor-model", "options", "log-level", "test", "launch", "quit"]
|
|
13441
|
-
action = actions[main_idx]
|
|
13555
|
+
actions = ["language", "provider", "api-key", "base-url", "model", "advisor-model", "options", "channel-delivery", "log-level", "test", "launch", "quit"]
|
|
13556
|
+
action = actions[main_idx]
|
|
13442
13557
|
if action == "launch":
|
|
13443
13558
|
blockers = launch_readiness_errors()
|
|
13444
13559
|
if blockers:
|
|
@@ -13563,6 +13678,10 @@ def claude_channels_requested(cfg: dict[str, Any], passthrough: list[str], extra
|
|
|
13563
13678
|
return native_channel_passthrough_requested(passthrough)
|
|
13564
13679
|
|
|
13565
13680
|
|
|
13681
|
+
def should_use_native_channel_bridge(use_router_mode: bool, cfg: dict[str, Any], passthrough: list[str]) -> bool:
|
|
13682
|
+
return bool(use_router_mode and channel_delivery_mode(cfg) == "native" and not native_channel_passthrough_requested(passthrough))
|
|
13683
|
+
|
|
13684
|
+
|
|
13566
13685
|
def write_web_tools_mcp_config(cfg: dict[str, Any]) -> Path:
|
|
13567
13686
|
web = cfg.get("web_search", {})
|
|
13568
13687
|
package = web.get("package") or "ddg-mcp-search"
|
|
@@ -13674,8 +13793,12 @@ def write_mcp_proxy_config(
|
|
|
13674
13793
|
return MCP_PROXY_CONFIG
|
|
13675
13794
|
|
|
13676
13795
|
|
|
13677
|
-
def should_use_channel_stdin_proxy(use_router_mode: bool, passthrough: list[str]) -> bool:
|
|
13678
|
-
|
|
13796
|
+
def should_use_channel_stdin_proxy(use_router_mode: bool, passthrough: list[str], cfg: dict[str, Any] | None = None) -> bool:
|
|
13797
|
+
if not use_router_mode or native_channel_passthrough_requested(passthrough):
|
|
13798
|
+
return False
|
|
13799
|
+
if cfg is not None and channel_delivery_mode(cfg) == "native":
|
|
13800
|
+
return False
|
|
13801
|
+
return True
|
|
13679
13802
|
|
|
13680
13803
|
|
|
13681
13804
|
def format_channel_wake_prompt(message: dict[str, Any]) -> str:
|
|
@@ -14490,7 +14613,9 @@ def launch_claude(
|
|
|
14490
14613
|
env["PATH"] = str(HOME / ".local" / "bin") + os.pathsep + env.get("PATH", "")
|
|
14491
14614
|
launch_env = env_vars(cfg)
|
|
14492
14615
|
launch_passthrough = normalize_channel_passthrough(passthrough)
|
|
14493
|
-
|
|
14616
|
+
native_channel_bridge = should_use_native_channel_bridge(use_router_mode, cfg, launch_passthrough)
|
|
14617
|
+
stdin_channel_proxy = should_use_channel_stdin_proxy(use_router_mode, launch_passthrough, cfg)
|
|
14618
|
+
if claude_channels_requested(cfg, launch_passthrough) or native_channel_bridge:
|
|
14494
14619
|
env.pop("CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS", None)
|
|
14495
14620
|
launch_env.pop("CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS", None)
|
|
14496
14621
|
if use_native_anthropic:
|
|
@@ -14525,8 +14650,10 @@ def launch_claude(
|
|
|
14525
14650
|
mcp_config_paths: list[str] = []
|
|
14526
14651
|
if should_attach_web_search(provider, cfg, web_search_override):
|
|
14527
14652
|
mcp_config_paths.append(str(write_duckduckgo_mcp_config(cfg)))
|
|
14653
|
+
if native_channel_bridge:
|
|
14654
|
+
mcp_config_paths.append(str(write_channel_mcp_config()))
|
|
14528
14655
|
claude_passthrough = list(launch_passthrough)
|
|
14529
|
-
if
|
|
14656
|
+
if stdin_channel_proxy or native_channel_bridge:
|
|
14530
14657
|
auto_start_sse_channels_from_mcp_configs(launch_passthrough)
|
|
14531
14658
|
proxy_config = write_mcp_proxy_config(
|
|
14532
14659
|
launch_passthrough,
|
|
@@ -14549,7 +14676,7 @@ def launch_claude(
|
|
|
14549
14676
|
cmd.extend(["--model", model])
|
|
14550
14677
|
cmd.extend(extra_args)
|
|
14551
14678
|
cmd.extend(claude_passthrough)
|
|
14552
|
-
if
|
|
14679
|
+
if stdin_channel_proxy:
|
|
14553
14680
|
return subprocess_call_with_channel_wake_proxy(cmd, env)
|
|
14554
14681
|
return subprocess.call(cmd, env=env)
|
|
14555
14682
|
|
|
@@ -14574,7 +14701,9 @@ Control plane, runs before Claude Code and does not require LLM connectivity:
|
|
|
14574
14701
|
claude-any web-fetch [on|off] Auto-attach fetch MCP for web page content
|
|
14575
14702
|
claude-any log-level [LEVEL] Show or set router log level
|
|
14576
14703
|
claude-any channels [cmd] Configure external channel specs
|
|
14577
|
-
claude-any
|
|
14704
|
+
claude-any channel-delivery [stdin|native]
|
|
14705
|
+
Select PTY wake proxy or native claude/channel bridge
|
|
14706
|
+
claude-any ollama-native [on|off] Use Ollama's official Claude Code env path
|
|
14578
14707
|
claude-any ollama-options [provider] [key=value ...]
|
|
14579
14708
|
Set Ollama num_ctx/options/keep_alive/think
|
|
14580
14709
|
claude-any provider-options [provider] [key=value ...]
|
|
@@ -14614,6 +14743,8 @@ Headless setup flags, namespaced to avoid Claude CLI collisions:
|
|
|
14614
14743
|
claude-any --ca-web-fetch Enable fetch MCP
|
|
14615
14744
|
claude-any --ca-no-web-fetch Disable fetch MCP
|
|
14616
14745
|
claude-any --ca-channel SPEC Add an official/approved Claude Code channel
|
|
14746
|
+
claude-any --ca-channel-delivery MODE
|
|
14747
|
+
Set channel delivery: stdin or native
|
|
14617
14748
|
claude-any --ca-clear-channels Clear saved channel specs
|
|
14618
14749
|
claude-any --ca-no-self-update-check
|
|
14619
14750
|
Skip Claude Any npm self-update check
|
|
@@ -14738,6 +14869,10 @@ def apply_headless_env_config() -> tuple[bool, bool | None, bool | None, bool |
|
|
|
14738
14869
|
for channel_value in dev_channel_values:
|
|
14739
14870
|
add_channel_spec(channel_value)
|
|
14740
14871
|
skip_menu = True
|
|
14872
|
+
channel_delivery = os.environ.get("CLAUDE_ANY_CHANNEL_DELIVERY", "").strip()
|
|
14873
|
+
if channel_delivery:
|
|
14874
|
+
set_channel_delivery_config(channel_delivery)
|
|
14875
|
+
skip_menu = True
|
|
14741
14876
|
return skip_menu, web_search_override, update_check_override, self_update_check_override, force_menu
|
|
14742
14877
|
|
|
14743
14878
|
|
|
@@ -14805,9 +14940,16 @@ def run_cli(argv: list[str]) -> int:
|
|
|
14805
14940
|
if head in ("channels", "channel"):
|
|
14806
14941
|
cmd_channels(argparse.Namespace(values=rest))
|
|
14807
14942
|
return 0
|
|
14808
|
-
if head in ("
|
|
14809
|
-
|
|
14810
|
-
|
|
14943
|
+
if head in ("channel-delivery", "channel_delivery"):
|
|
14944
|
+
if rest:
|
|
14945
|
+
for line in set_channel_delivery_config(rest[0]):
|
|
14946
|
+
print(line)
|
|
14947
|
+
else:
|
|
14948
|
+
print(f"channel_delivery: {channel_delivery_mode()}")
|
|
14949
|
+
return 0
|
|
14950
|
+
if head in ("ollama-native", "ollama-compat"):
|
|
14951
|
+
cmd_ollama_native(argparse.Namespace(value=rest[0] if rest else None))
|
|
14952
|
+
return 0
|
|
14811
14953
|
if head in ("ollama-options", "ollama-option", "ollama-opts"):
|
|
14812
14954
|
cmd_ollama_options(argparse.Namespace(values=rest))
|
|
14813
14955
|
return 0
|
|
@@ -15163,6 +15305,18 @@ def run_cli(argv: list[str]) -> int:
|
|
|
15163
15305
|
for line in add_channel_spec(value):
|
|
15164
15306
|
print(line)
|
|
15165
15307
|
skip_menu = True
|
|
15308
|
+
elif arg == "--ca-channel-delivery" or arg.startswith("--ca-channel-delivery="):
|
|
15309
|
+
value = arg.split("=", 1)[1] if "=" in arg else None
|
|
15310
|
+
if value is None:
|
|
15311
|
+
if i + 1 >= len(argv):
|
|
15312
|
+
raise SystemExit("Missing mode for --ca-channel-delivery")
|
|
15313
|
+
value = argv[i + 1]
|
|
15314
|
+
i += 2
|
|
15315
|
+
else:
|
|
15316
|
+
i += 1
|
|
15317
|
+
for line in set_channel_delivery_config(value):
|
|
15318
|
+
print(line)
|
|
15319
|
+
skip_menu = True
|
|
15166
15320
|
elif arg == "--ca-dev-channel" or arg.startswith("--ca-dev-channel="):
|
|
15167
15321
|
value = arg.split("=", 1)[1] if "=" in arg else None
|
|
15168
15322
|
if value is None:
|
|
@@ -15268,7 +15422,10 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
15268
15422
|
ch = sub.add_parser("channels")
|
|
15269
15423
|
ch.add_argument("values", nargs="*")
|
|
15270
15424
|
ch.set_defaults(func=cmd_channels)
|
|
15271
|
-
|
|
15425
|
+
cd = sub.add_parser("channel-delivery")
|
|
15426
|
+
cd.add_argument("value", nargs="?")
|
|
15427
|
+
cd.set_defaults(func=cmd_channel_delivery)
|
|
15428
|
+
on = sub.add_parser("ollama-native")
|
|
15272
15429
|
on.add_argument("value", nargs="?")
|
|
15273
15430
|
on.set_defaults(func=cmd_ollama_native)
|
|
15274
15431
|
oo = sub.add_parser("ollama-options")
|
package/package.json
CHANGED