@oneciel-ai/claude-any 0.1.74 → 0.1.76

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.
Files changed (2) hide show
  1. package/claude_any.py +96 -54
  2. package/package.json +1 -1
package/claude_any.py CHANGED
@@ -102,7 +102,7 @@ OFFICIAL_CHANNEL_PLUGINS = {
102
102
  "fakechat": "plugin:fakechat@claude-plugins-official",
103
103
  }
104
104
  APP_NAME = "Claude Any"
105
- VERSION = "0.1.74"
105
+ VERSION = "0.1.76"
106
106
  CREDITS = "Credits: One Ciel LLC"
107
107
 
108
108
  LOG_LEVELS = {"SILENT": 0, "ERROR": 1, "WARN": 2, "INFO": 3, "DEBUG": 4, "TRACE": 5}
@@ -8763,25 +8763,16 @@ def is_channel_spec_tagged(spec: str) -> bool:
8763
8763
  return spec.startswith("plugin:") or spec.startswith("server:")
8764
8764
 
8765
8765
 
8766
- def channel_development_enabled(cfg: dict[str, Any] | None = None) -> bool:
8767
- cfg = cfg or load_config()
8768
- return bool(cfg.setdefault("claude_code", {}).get("development_channels", False))
8769
-
8770
-
8771
8766
  def channel_status_text(cfg: dict[str, Any] | None = None) -> str:
8772
8767
  cfg = cfg or load_config()
8773
8768
  channels = channel_specs(cfg)
8774
8769
  if not channels:
8775
8770
  return "off"
8776
- suffix = "; dev" if channel_development_enabled(cfg) else ""
8777
- return f"{len(channels)} channel{'s' if len(channels) != 1 else ''}{suffix}"
8771
+ return f"{len(channels)} channel{'s' if len(channels) != 1 else ''}"
8778
8772
 
8779
8773
 
8780
8774
  def set_channel_development_enabled(enabled: bool) -> list[str]:
8781
- cfg = load_config()
8782
- cfg.setdefault("claude_code", {})["development_channels"] = bool(enabled)
8783
- save_config(cfg)
8784
- return [f"Development channels: {'on' if enabled else 'off'}."]
8775
+ return ["Channel wake delivery is always enabled by Claude Any."]
8785
8776
 
8786
8777
 
8787
8778
  def add_channel_spec(spec: str, *, development: bool = False) -> list[str]:
@@ -8796,13 +8787,8 @@ def add_channel_spec(spec: str, *, development: bool = False) -> list[str]:
8796
8787
  if spec not in channels:
8797
8788
  channels.append(spec)
8798
8789
  cc["channels"] = channels
8799
- if development:
8800
- cc["development_channels"] = True
8801
8790
  save_config(cfg)
8802
- lines = [f"Channel added: {spec}."]
8803
- if development:
8804
- lines.append("Development channels: on.")
8805
- return lines
8791
+ return [f"Channel added: {spec}."]
8806
8792
 
8807
8793
 
8808
8794
  def remove_channel_spec(spec: str) -> list[str]:
@@ -8833,7 +8819,6 @@ def cmd_channels(args: argparse.Namespace) -> None:
8833
8819
  for spec in channel_specs(cfg):
8834
8820
  if spec not in OFFICIAL_CHANNEL_PLUGINS.values():
8835
8821
  print(f" * custom {spec}")
8836
- print(f"development_channels: {'on' if channel_development_enabled(cfg) else 'off'}")
8837
8822
  return
8838
8823
  head = values[0].strip().lower()
8839
8824
  if head in ("on", "enable", "add"):
@@ -8844,13 +8829,12 @@ def cmd_channels(args: argparse.Namespace) -> None:
8844
8829
  return
8845
8830
  if head in ("dev", "development"):
8846
8831
  if len(values) >= 2 and values[1].lower() in ("on", "off", "true", "false", "1", "0"):
8847
- enabled = values[1].lower() in ("on", "true", "1")
8848
- for line in set_channel_development_enabled(enabled):
8832
+ for line in set_channel_development_enabled(True):
8849
8833
  print(line)
8850
8834
  return
8851
8835
  if len(values) < 2:
8852
- raise SystemExit("Usage: claude-any channels dev CHANNEL_SPEC | claude-any channels dev on|off")
8853
- for line in add_channel_spec(values[1], development=True):
8836
+ raise SystemExit("Usage: claude-any channels add CHANNEL_SPEC")
8837
+ for line in add_channel_spec(values[1]):
8854
8838
  print(line)
8855
8839
  return
8856
8840
  if head in ("off", "disable", "remove", "rm"):
@@ -12138,16 +12122,13 @@ def advisor_model_panel_rows(provider: str, pcfg: dict[str, Any]) -> tuple[list[
12138
12122
 
12139
12123
  def channel_panel_rows(cfg: dict[str, Any]) -> tuple[list[str], list[str]]:
12140
12124
  channels = channel_specs(cfg)
12141
- dev_enabled = channel_development_enabled(cfg)
12142
12125
  rows: list[str] = []
12143
12126
  values: list[str] = []
12144
- rows.append(f"Development channel loading [{'on' if dev_enabled else 'off'}]")
12145
- values.append("__toggle_dev__")
12146
12127
  for name, spec in OFFICIAL_CHANNEL_PLUGINS.items():
12147
12128
  mark = "*" if spec in channels else " "
12148
12129
  rows.append(f"{mark} {name:<10} {spec}")
12149
12130
  values.append(spec)
12150
- rows.append("+ Add development/custom channel...")
12131
+ rows.append("+ Add custom channel...")
12151
12132
  values.append("__add_custom__")
12152
12133
  if channels:
12153
12134
  rows.append("- Remove channel...")
@@ -12683,15 +12664,10 @@ def portable_prelaunch_menu() -> int:
12683
12664
  elif panel == "channels":
12684
12665
  if value == "back":
12685
12666
  close_panel()
12686
- elif value == "__toggle_dev__":
12687
- messages = set_channel_development_enabled(not channel_development_enabled(cfg))
12688
- cfg = load_config()
12689
- panel_rows, panel_values = channel_panel_rows(cfg)
12690
- panel_idx = 0
12691
12667
  elif value == "__add_custom__":
12692
12668
  spec = prompt_menu_value("Channel spec (for example plugin:ainet@local or server:ainet)", restore_tty=restore_line_mode, raw_tty=restore_raw_mode)
12693
12669
  if spec:
12694
- messages = add_channel_spec(spec, development=True)
12670
+ messages = add_channel_spec(spec)
12695
12671
  cfg = load_config()
12696
12672
  panel_rows, panel_values = channel_panel_rows(cfg)
12697
12673
  elif value == "__remove__":
@@ -12906,13 +12882,42 @@ def has_passthrough_option(passthrough: list[str], *names: str) -> bool:
12906
12882
  return any(arg in names or any(arg.startswith(name + "=") for name in names) for arg in passthrough)
12907
12883
 
12908
12884
 
12885
+ def normalize_channel_passthrough(passthrough: list[str]) -> list[str]:
12886
+ normalized: list[str] = []
12887
+ i = 0
12888
+ while i < len(passthrough):
12889
+ arg = passthrough[i]
12890
+ if arg == "--channels":
12891
+ normalized.append("--dangerously-load-development-channels")
12892
+ i += 1
12893
+ while i < len(passthrough) and is_channel_spec_tagged(passthrough[i]):
12894
+ normalized.append(passthrough[i])
12895
+ i += 1
12896
+ continue
12897
+ if arg.startswith("--channels="):
12898
+ value = arg.split("=", 1)[1].strip()
12899
+ if value:
12900
+ normalized.extend(["--dangerously-load-development-channels", value])
12901
+ else:
12902
+ normalized.append("--dangerously-load-development-channels")
12903
+ i += 1
12904
+ continue
12905
+ normalized.append(arg)
12906
+ i += 1
12907
+ return normalized
12908
+
12909
+
12909
12910
  def claude_channel_args(cfg: dict[str, Any], passthrough: list[str]) -> list[str]:
12910
12911
  channels = [spec for spec in channel_specs(cfg) if is_channel_spec_tagged(spec)]
12911
12912
  if not channels or has_passthrough_option(passthrough, "--channels", "--dangerously-load-development-channels"):
12912
12913
  return []
12913
- if channel_development_enabled(cfg):
12914
- return ["--dangerously-load-development-channels", *channels]
12915
- return ["--channels", *channels]
12914
+ return ["--dangerously-load-development-channels", *channels]
12915
+
12916
+
12917
+ def claude_channels_requested(cfg: dict[str, Any], passthrough: list[str]) -> bool:
12918
+ if has_passthrough_option(passthrough, "--channels", "--dangerously-load-development-channels"):
12919
+ return True
12920
+ return any(is_channel_spec_tagged(spec) for spec in channel_specs(cfg))
12916
12921
 
12917
12922
 
12918
12923
  def write_web_tools_mcp_config(cfg: dict[str, Any]) -> Path:
@@ -13047,6 +13052,29 @@ def npm_latest_package_version(npm: str, package_spec: str, timeout: float = 8.0
13047
13052
  return out.splitlines()[-1].strip() if out else ""
13048
13053
 
13049
13054
 
13055
+ def npm_global_package_root(npm: str, package_name: str = "@oneciel-ai/claude-any", timeout: float = 8.0) -> Path | None:
13056
+ try:
13057
+ p = subprocess.run(
13058
+ [npm, "root", "-g"],
13059
+ text=True,
13060
+ stdout=subprocess.PIPE,
13061
+ stderr=subprocess.DEVNULL,
13062
+ timeout=timeout,
13063
+ )
13064
+ except Exception:
13065
+ return None
13066
+ if p.returncode != 0:
13067
+ return None
13068
+ root = (p.stdout or "").strip()
13069
+ if not root:
13070
+ return None
13071
+ package_path = Path(root)
13072
+ for part in package_name.split("/"):
13073
+ if part:
13074
+ package_path /= part
13075
+ return package_path
13076
+
13077
+
13050
13078
  def claude_code_current_version(claude: str) -> str:
13051
13079
  try:
13052
13080
  p = subprocess.run(
@@ -13071,6 +13099,26 @@ def running_from_npm_package() -> bool:
13071
13099
  return "/node_modules/@oneciel-ai/claude-any/" in path
13072
13100
 
13073
13101
 
13102
+ def claude_any_restart_user_args() -> list[str]:
13103
+ args = list(sys.argv[1:])
13104
+ if args and args[0] == "cli":
13105
+ return args[1:]
13106
+ return args
13107
+
13108
+
13109
+ def restart_claude_any_after_update(npm: str) -> None:
13110
+ os.environ["CLAUDE_ANY_SKIP_SELF_UPDATE"] = "1"
13111
+ user_args = claude_any_restart_user_args()
13112
+ package_root = npm_global_package_root(npm)
13113
+ package_script = package_root / "claude_any.py" if package_root else None
13114
+ if package_script and package_script.exists():
13115
+ os.execv(sys.executable, [sys.executable, str(package_script), "cli", *user_args])
13116
+ launcher = find_executable("claude-any")
13117
+ if launcher:
13118
+ raise SystemExit(subprocess.call([launcher, *user_args], env=os.environ.copy()))
13119
+ os.execv(sys.executable, [sys.executable, *sys.argv])
13120
+
13121
+
13074
13122
  def run_claude_any_update_check(enabled: bool = True) -> bool:
13075
13123
  if not enabled:
13076
13124
  return False
@@ -13113,9 +13161,10 @@ def run_claude_any_update_check(enabled: bool = True) -> bool:
13113
13161
  print(f"Claude Any update exited with {update.returncode}; continuing with current version.", flush=True)
13114
13162
  return False
13115
13163
  print("Claude Any updated. Restarting with the new version...", flush=True)
13116
- os.environ["CLAUDE_ANY_SKIP_SELF_UPDATE"] = "1"
13117
13164
  try:
13118
- os.execv(sys.executable, [sys.executable, *sys.argv])
13165
+ restart_claude_any_after_update(npm)
13166
+ except SystemExit:
13167
+ raise
13119
13168
  except Exception as exc:
13120
13169
  print(f"Restart failed ({type(exc).__name__}); continuing with the current process.", flush=True)
13121
13170
  return True
@@ -13221,6 +13270,9 @@ def launch_claude(
13221
13270
  env = os.environ.copy()
13222
13271
  env["PATH"] = str(HOME / ".local" / "bin") + os.pathsep + env.get("PATH", "")
13223
13272
  launch_env = env_vars(cfg)
13273
+ launch_passthrough = normalize_channel_passthrough(passthrough)
13274
+ if claude_channels_requested(cfg, launch_passthrough):
13275
+ launch_env.pop("CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS", None)
13224
13276
  if use_native_anthropic:
13225
13277
  for key in (
13226
13278
  "ANTHROPIC_BASE_URL",
@@ -13252,9 +13304,9 @@ def launch_claude(
13252
13304
  extra_args: list[str] = []
13253
13305
  if should_attach_web_search(provider, cfg, web_search_override):
13254
13306
  extra_args.extend(["--mcp-config", str(write_duckduckgo_mcp_config(cfg))])
13255
- if should_append_compat_prompt(provider, cfg) and not has_passthrough_option(passthrough, "--system-prompt"):
13307
+ if should_append_compat_prompt(provider, cfg) and not has_passthrough_option(launch_passthrough, "--system-prompt"):
13256
13308
  extra_args.extend(["--append-system-prompt", NON_ANTHROPIC_COMPAT_PROMPT])
13257
- extra_args.extend(claude_channel_args(cfg, passthrough))
13309
+ extra_args.extend(claude_channel_args(cfg, launch_passthrough))
13258
13310
  cmd = [
13259
13311
  claude,
13260
13312
  "--dangerously-skip-permissions",
@@ -13263,7 +13315,7 @@ def launch_claude(
13263
13315
  if model:
13264
13316
  cmd.extend(["--model", model])
13265
13317
  cmd.extend(extra_args)
13266
- cmd.extend(passthrough)
13318
+ cmd.extend(launch_passthrough)
13267
13319
  return subprocess.call(cmd, env=env)
13268
13320
 
13269
13321
 
@@ -13325,9 +13377,6 @@ Headless setup flags, namespaced to avoid Claude CLI collisions:
13325
13377
  claude-any --ca-web-fetch Enable fetch MCP
13326
13378
  claude-any --ca-no-web-fetch Disable fetch MCP
13327
13379
  claude-any --ca-channel SPEC Add an official/approved Claude Code channel
13328
- claude-any --ca-dev-channel SPEC Add a development channel and enable dev loading
13329
- claude-any --ca-development-channels on|off
13330
- Use tagged specs with --dangerously-load-development-channels
13331
13380
  claude-any --ca-clear-channels Clear saved channel auto-injection specs
13332
13381
  claude-any --ca-no-self-update-check
13333
13382
  Skip Claude Any npm self-update check
@@ -13450,13 +13499,7 @@ def apply_headless_env_config() -> tuple[bool, bool | None, bool | None, bool |
13450
13499
  if item.strip()
13451
13500
  ]
13452
13501
  for channel_value in dev_channel_values:
13453
- add_channel_spec(channel_value, development=True)
13454
- skip_menu = True
13455
- dev_channels = os.environ.get("CLAUDE_ANY_DEVELOPMENT_CHANNELS", "").strip().lower()
13456
- if dev_channels:
13457
- if dev_channels not in ("on", "off", "true", "false", "1", "0"):
13458
- raise SystemExit("CLAUDE_ANY_DEVELOPMENT_CHANNELS must be on or off")
13459
- set_channel_development_enabled(dev_channels in ("on", "true", "1"))
13502
+ add_channel_spec(channel_value)
13460
13503
  skip_menu = True
13461
13504
  return skip_menu, web_search_override, update_check_override, self_update_check_override, force_menu
13462
13505
 
@@ -13875,7 +13918,7 @@ def run_cli(argv: list[str]) -> int:
13875
13918
  i += 2
13876
13919
  else:
13877
13920
  i += 1
13878
- for line in add_channel_spec(value, development=True):
13921
+ for line in add_channel_spec(value):
13879
13922
  print(line)
13880
13923
  skip_menu = True
13881
13924
  elif arg == "--ca-development-channels" or arg.startswith("--ca-development-channels="):
@@ -13887,8 +13930,7 @@ def run_cli(argv: list[str]) -> int:
13887
13930
  i += 2
13888
13931
  else:
13889
13932
  i += 1
13890
- enabled = value.strip().lower() in ("on", "enable", "enabled", "true", "1")
13891
- for line in set_channel_development_enabled(enabled):
13933
+ for line in set_channel_development_enabled(True):
13892
13934
  print(line)
13893
13935
  skip_menu = True
13894
13936
  elif arg == "--ca-clear-channels":
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oneciel-ai/claude-any",
3
- "version": "0.1.74",
3
+ "version": "0.1.76",
4
4
  "description": "Claude Code provider selector for Anthropic, Ollama, Ollama Cloud, vLLM, NVIDIA hosted, and self-hosted NIM.",
5
5
  "license": "MIT",
6
6
  "author": "One Ciel LLC",