@oneciel-ai/claude-any 0.1.69 → 0.1.71
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/README.md +17 -1
- package/claude_any.py +93 -8
- package/docs/README.ja.md +17 -1
- package/docs/README.ko.md +17 -1
- package/docs/README.zh.md +15 -1
- package/docs/manual.md +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -68,7 +68,7 @@ arguments through unchanged.
|
|
|
68
68
|
|
|
69
69
|
Credits: One Ciel LLC
|
|
70
70
|
|
|
71
|
-
Current version: `0.1.
|
|
71
|
+
Current version: `0.1.71`
|
|
72
72
|
|
|
73
73
|
## Why This Exists
|
|
74
74
|
|
|
@@ -495,6 +495,22 @@ steps under that larger model's supervision.
|
|
|
495
495
|
|
|
496
496
|
## Changelog
|
|
497
497
|
|
|
498
|
+
### 0.1.71
|
|
499
|
+
|
|
500
|
+
- **MCP SSE channel initialization**: the channel bridge now handles MCP
|
|
501
|
+
`endpoint` events by sending `initialize` and `notifications/initialized`,
|
|
502
|
+
so AI-Net style push notifications can start flowing into Claude Any.
|
|
503
|
+
- **Channel SSE diagnostics**: connector status now reports the MCP endpoint,
|
|
504
|
+
initialization state, and last MCP initialization error.
|
|
505
|
+
|
|
506
|
+
### 0.1.70
|
|
507
|
+
|
|
508
|
+
- **Linux menu debug log fix**: key-debug logging now writes under the user's
|
|
509
|
+
Claude Any config directory instead of global `/tmp`, avoiding permission
|
|
510
|
+
crashes on locked-down Linux systems.
|
|
511
|
+
- **Best-effort key logging**: menu input no longer fails if the optional
|
|
512
|
+
key-debug log cannot be written.
|
|
513
|
+
|
|
498
514
|
### 0.1.69
|
|
499
515
|
|
|
500
516
|
- **Realtime channel bridge**: added `/ca/channel/messages`, `/ca/channel/wait`,
|
package/claude_any.py
CHANGED
|
@@ -51,6 +51,7 @@ CONTEXT_USAGE_PATH = CONFIG_DIR / "context-usage.json"
|
|
|
51
51
|
OLLAMA_MODEL_CATALOG_PATH = CONFIG_DIR / "ollama-model-catalog.json"
|
|
52
52
|
CHAT_MESSAGES_PATH = CONFIG_DIR / "chat-messages.jsonl"
|
|
53
53
|
CHAT_FILES_DIR = CONFIG_DIR / "chat-files"
|
|
54
|
+
MENU_KEY_DEBUG_PATH = CONFIG_DIR / "ca-key-debug.log"
|
|
54
55
|
PLAN_ARTIFACTS_DIR = CONFIG_DIR / "plan-artifacts"
|
|
55
56
|
PID_PATH = CONFIG_DIR / "router.pid"
|
|
56
57
|
MODEL_LIST_CACHE_PATH = CONFIG_DIR / "model-list-cache.json"
|
|
@@ -95,7 +96,7 @@ PROVIDER_LABELS = {
|
|
|
95
96
|
"self-hosted-nim": "Self Hosted NIM",
|
|
96
97
|
}
|
|
97
98
|
APP_NAME = "Claude Any"
|
|
98
|
-
VERSION = "0.1.
|
|
99
|
+
VERSION = "0.1.71"
|
|
99
100
|
CREDITS = "Credits: One Ciel LLC"
|
|
100
101
|
|
|
101
102
|
LOG_LEVELS = {"SILENT": 0, "ERROR": 1, "WARN": 2, "INFO": 3, "DEBUG": 4, "TRACE": 5}
|
|
@@ -4337,6 +4338,9 @@ def _channel_sse_status_public(name: str, state: dict[str, Any]) -> dict[str, An
|
|
|
4337
4338
|
"messages_received": int(state.get("messages_received") or 0),
|
|
4338
4339
|
"event_filter": state.get("event_filter") or [],
|
|
4339
4340
|
"read_timeout_seconds": state.get("read_timeout_seconds"),
|
|
4341
|
+
"mcp_endpoint": state.get("mcp_endpoint"),
|
|
4342
|
+
"mcp_initialized": bool(state.get("mcp_initialized")),
|
|
4343
|
+
"mcp_last_error": state.get("mcp_last_error"),
|
|
4340
4344
|
"last_error": state.get("last_error"),
|
|
4341
4345
|
}
|
|
4342
4346
|
|
|
@@ -4401,8 +4405,78 @@ def _sse_payload_to_chat_payload(data_text: str, event_name: str, defaults: dict
|
|
|
4401
4405
|
}
|
|
4402
4406
|
|
|
4403
4407
|
|
|
4408
|
+
def _channel_sse_set_state(name: str, **updates: Any) -> None:
|
|
4409
|
+
with _CHANNEL_SSE_LOCK:
|
|
4410
|
+
state = _CHANNEL_SSE_CONNECTIONS.get(name)
|
|
4411
|
+
if state:
|
|
4412
|
+
state.update(updates)
|
|
4413
|
+
|
|
4414
|
+
|
|
4415
|
+
def _channel_sse_absolute_endpoint(stream_url: str, endpoint: str) -> str:
|
|
4416
|
+
endpoint = (endpoint or "").strip()
|
|
4417
|
+
if endpoint.startswith(("http://", "https://")):
|
|
4418
|
+
return endpoint
|
|
4419
|
+
return urllib.parse.urljoin(stream_url, endpoint)
|
|
4420
|
+
|
|
4421
|
+
|
|
4422
|
+
def _mcp_sse_post_json(endpoint: str, headers: dict[str, str], payload: dict[str, Any], timeout: float) -> Any:
|
|
4423
|
+
request_headers = {**headers, "Content-Type": "application/json", "Accept": "application/json, text/event-stream"}
|
|
4424
|
+
req = urllib.request.Request(
|
|
4425
|
+
endpoint,
|
|
4426
|
+
data=json.dumps(payload, ensure_ascii=False).encode("utf-8"),
|
|
4427
|
+
headers=request_headers,
|
|
4428
|
+
method="POST",
|
|
4429
|
+
)
|
|
4430
|
+
with urllib.request.urlopen(req, timeout=timeout) as response:
|
|
4431
|
+
data = response.read()
|
|
4432
|
+
if not data:
|
|
4433
|
+
return None
|
|
4434
|
+
try:
|
|
4435
|
+
return json.loads(data.decode("utf-8"))
|
|
4436
|
+
except Exception:
|
|
4437
|
+
return data.decode("utf-8", errors="replace")
|
|
4438
|
+
|
|
4439
|
+
|
|
4440
|
+
def _channel_sse_maybe_initialize_mcp(name: str, endpoint_text: str) -> None:
|
|
4441
|
+
with _CHANNEL_SSE_LOCK:
|
|
4442
|
+
state = _CHANNEL_SSE_CONNECTIONS.get(name)
|
|
4443
|
+
if not state:
|
|
4444
|
+
return
|
|
4445
|
+
if not bool(state.get("mcp_enabled", True)):
|
|
4446
|
+
return
|
|
4447
|
+
if state.get("mcp_initialized"):
|
|
4448
|
+
return
|
|
4449
|
+
stream_url = str(state.get("url") or "")
|
|
4450
|
+
headers = dict(state.get("headers") or {})
|
|
4451
|
+
timeout = max(5.0, min(120.0, float(state.get("mcp_timeout_seconds") or 20.0)))
|
|
4452
|
+
protocol_version = str(state.get("mcp_protocol_version") or "2024-11-05")
|
|
4453
|
+
endpoint = _channel_sse_absolute_endpoint(stream_url, endpoint_text)
|
|
4454
|
+
try:
|
|
4455
|
+
initialize = {
|
|
4456
|
+
"jsonrpc": "2.0",
|
|
4457
|
+
"id": 1,
|
|
4458
|
+
"method": "initialize",
|
|
4459
|
+
"params": {
|
|
4460
|
+
"protocolVersion": protocol_version,
|
|
4461
|
+
"capabilities": {},
|
|
4462
|
+
"clientInfo": {"name": "claude-any-channel-bridge", "version": VERSION},
|
|
4463
|
+
},
|
|
4464
|
+
}
|
|
4465
|
+
_mcp_sse_post_json(endpoint, headers, initialize, timeout)
|
|
4466
|
+
initialized = {"jsonrpc": "2.0", "method": "notifications/initialized", "params": {}}
|
|
4467
|
+
_mcp_sse_post_json(endpoint, headers, initialized, timeout)
|
|
4468
|
+
_channel_sse_set_state(name, mcp_endpoint=endpoint, mcp_initialized=True, mcp_last_error=None)
|
|
4469
|
+
router_log("INFO", f"channel_sse_mcp_initialized name={name} endpoint={endpoint}")
|
|
4470
|
+
except Exception as exc:
|
|
4471
|
+
_channel_sse_set_state(name, mcp_endpoint=endpoint, mcp_initialized=False, mcp_last_error=f"{type(exc).__name__}: {exc}")
|
|
4472
|
+
router_log("WARN", f"channel_sse_mcp_initialize_failed name={name} endpoint={endpoint} error={type(exc).__name__}: {exc}")
|
|
4473
|
+
|
|
4474
|
+
|
|
4404
4475
|
def _channel_sse_dispatch(name: str, event_name: str, data_lines: list[str]) -> None:
|
|
4405
4476
|
data_text = "\n".join(data_lines)
|
|
4477
|
+
if (event_name or "").strip().lower() == "endpoint":
|
|
4478
|
+
_channel_sse_maybe_initialize_mcp(name, data_text)
|
|
4479
|
+
return
|
|
4406
4480
|
with _CHANNEL_SSE_LOCK:
|
|
4407
4481
|
state = _CHANNEL_SSE_CONNECTIONS.get(name)
|
|
4408
4482
|
if not state:
|
|
@@ -4510,6 +4584,12 @@ def start_channel_sse_connection(config: dict[str, Any]) -> dict[str, Any]:
|
|
|
4510
4584
|
"event_filter": event_filter,
|
|
4511
4585
|
"read_timeout_seconds": float(config.get("read_timeout_seconds") or config.get("timeout") or 300.0),
|
|
4512
4586
|
"retry_seconds": float(config.get("retry_seconds") or 5.0),
|
|
4587
|
+
"mcp_enabled": bool(config.get("mcp", config.get("mcp_enabled", True))),
|
|
4588
|
+
"mcp_endpoint": None,
|
|
4589
|
+
"mcp_initialized": False,
|
|
4590
|
+
"mcp_last_error": None,
|
|
4591
|
+
"mcp_protocol_version": str(config.get("mcp_protocol_version") or "2024-11-05"),
|
|
4592
|
+
"mcp_timeout_seconds": float(config.get("mcp_timeout_seconds") or 20.0),
|
|
4513
4593
|
}
|
|
4514
4594
|
_CHANNEL_SSE_CONNECTIONS[name] = state
|
|
4515
4595
|
thread = threading.Thread(target=_channel_sse_worker, args=(name,), daemon=True, name=f"claude-any-channel-sse-{name}")
|
|
@@ -11693,6 +11773,15 @@ def print_intro_panel(width: int) -> None:
|
|
|
11693
11773
|
print("\n".join(intro_panel_lines(width)))
|
|
11694
11774
|
|
|
11695
11775
|
|
|
11776
|
+
def append_menu_key_debug_log(line: str) -> None:
|
|
11777
|
+
try:
|
|
11778
|
+
MENU_KEY_DEBUG_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
11779
|
+
with MENU_KEY_DEBUG_PATH.open("a", encoding="utf-8") as f:
|
|
11780
|
+
f.write(line)
|
|
11781
|
+
except OSError:
|
|
11782
|
+
pass
|
|
11783
|
+
|
|
11784
|
+
|
|
11696
11785
|
def read_menu_key(fd: int | None = None) -> str:
|
|
11697
11786
|
if os.name == "nt":
|
|
11698
11787
|
import msvcrt
|
|
@@ -11707,7 +11796,6 @@ def read_menu_key(fd: int | None = None) -> str:
|
|
|
11707
11796
|
return ch.lower()
|
|
11708
11797
|
|
|
11709
11798
|
import time
|
|
11710
|
-
debug_path = "/tmp/ca-key-debug.log"
|
|
11711
11799
|
if fd is None or fd < 0:
|
|
11712
11800
|
fd = sys.stdin.fileno()
|
|
11713
11801
|
ch = os.read(fd, 1)
|
|
@@ -11717,8 +11805,7 @@ def read_menu_key(fd: int | None = None) -> str:
|
|
|
11717
11805
|
b = os.read(fd, 1)
|
|
11718
11806
|
log += f" next={b!r}"
|
|
11719
11807
|
if not b:
|
|
11720
|
-
|
|
11721
|
-
f.write(log + " result='esc'\n")
|
|
11808
|
+
append_menu_key_debug_log(log + " result='esc'\n")
|
|
11722
11809
|
return "esc"
|
|
11723
11810
|
seq += b.decode("latin-1")
|
|
11724
11811
|
if b == b"[":
|
|
@@ -11741,15 +11828,13 @@ def read_menu_key(fd: int | None = None) -> str:
|
|
|
11741
11828
|
"\x1b[H": "home", "\x1b[F": "end",
|
|
11742
11829
|
}.get(seq, "esc")
|
|
11743
11830
|
log += f" seq={seq!r} result={result!r}"
|
|
11744
|
-
|
|
11745
|
-
f.write(log + "\n")
|
|
11831
|
+
append_menu_key_debug_log(log + "\n")
|
|
11746
11832
|
return result
|
|
11747
11833
|
if ch in (b"\r", b"\n"):
|
|
11748
11834
|
result = "enter"
|
|
11749
11835
|
else:
|
|
11750
11836
|
result = ch.decode("latin-1").lower()
|
|
11751
|
-
|
|
11752
|
-
f.write(log + f" result={result!r}\n")
|
|
11837
|
+
append_menu_key_debug_log(log + f" result={result!r}\n")
|
|
11753
11838
|
return result
|
|
11754
11839
|
|
|
11755
11840
|
|
package/docs/README.ja.md
CHANGED
|
@@ -61,7 +61,7 @@ vLLM、NVIDIA hosted、self-hosted NIM を選択し、通常の Claude Code 引
|
|
|
61
61
|
|
|
62
62
|
Credits: One Ciel LLC
|
|
63
63
|
|
|
64
|
-
現在のバージョン: `0.1.
|
|
64
|
+
現在のバージョン: `0.1.71`
|
|
65
65
|
|
|
66
66
|
## 作られた理由
|
|
67
67
|
|
|
@@ -365,6 +365,22 @@ Windows/Linux 管理、クリーンアップスクリプト、定期的なセキ
|
|
|
365
365
|
|
|
366
366
|
## 変更履歴
|
|
367
367
|
|
|
368
|
+
### 0.1.71
|
|
369
|
+
|
|
370
|
+
- **MCP SSE channel 初期化**: channel bridge は MCP `endpoint` event を受けると
|
|
371
|
+
`initialize` と `notifications/initialized` を自動送信し、AI-Net 型 push
|
|
372
|
+
notification が Claude Any に流れ始めるようになりました。
|
|
373
|
+
- **Channel SSE 診断情報**: connector status に MCP endpoint、初期化状態、
|
|
374
|
+
最後の MCP 初期化エラーを表示します。
|
|
375
|
+
|
|
376
|
+
### 0.1.70
|
|
377
|
+
|
|
378
|
+
- **Linux menu debug log 修正**: key-debug log は global `/tmp` ではなく
|
|
379
|
+
user の Claude Any config directory に書き込むようになり、制限された Linux
|
|
380
|
+
環境での permission crash を避けます。
|
|
381
|
+
- **best-effort key logging**: 任意の key-debug log を書けない場合でも menu
|
|
382
|
+
input は失敗しません。
|
|
383
|
+
|
|
368
384
|
### 0.1.69
|
|
369
385
|
|
|
370
386
|
- **リアルタイム channel bridge**: `/ca/channel/messages`, `/ca/channel/wait`,
|
package/docs/README.ko.md
CHANGED
|
@@ -67,7 +67,7 @@ NVIDIA hosted, self-hosted NIM을 선택하고, Claude Code의 일반 인자는
|
|
|
67
67
|
|
|
68
68
|
Credits: One Ciel LLC
|
|
69
69
|
|
|
70
|
-
현재 버전: `0.1.
|
|
70
|
+
현재 버전: `0.1.71`
|
|
71
71
|
|
|
72
72
|
## 왜 만들었나
|
|
73
73
|
|
|
@@ -371,6 +371,22 @@ Windows 이벤트 로그 리뷰, 바이러스/랜섬웨어 침입 시도 정리,
|
|
|
371
371
|
|
|
372
372
|
## 변경 이력
|
|
373
373
|
|
|
374
|
+
### 0.1.71
|
|
375
|
+
|
|
376
|
+
- **MCP SSE channel 초기화**: channel bridge가 MCP `endpoint` 이벤트를 받으면
|
|
377
|
+
`initialize`와 `notifications/initialized`를 자동 전송하므로, AI-Net 스타일
|
|
378
|
+
push notification이 Claude Any로 흐를 수 있습니다.
|
|
379
|
+
- **Channel SSE 진단 정보**: connector 상태에 MCP endpoint, 초기화 여부, 마지막
|
|
380
|
+
MCP 초기화 오류를 표시합니다.
|
|
381
|
+
|
|
382
|
+
### 0.1.70
|
|
383
|
+
|
|
384
|
+
- **Linux 메뉴 디버그 로그 수정**: key-debug 로그를 전역 `/tmp`가 아니라 사용자
|
|
385
|
+
Claude Any config 디렉터리 아래에 기록해, 제한된 Linux 환경에서 permission
|
|
386
|
+
crash가 발생하지 않도록 했습니다.
|
|
387
|
+
- **best-effort key logging**: 선택적 key-debug 로그를 쓸 수 없어도 메뉴 입력이
|
|
388
|
+
실패하지 않습니다.
|
|
389
|
+
|
|
374
390
|
### 0.1.69
|
|
375
391
|
|
|
376
392
|
- **실시간 채널 브리지**: `/ca/channel/messages`, `/ca/channel/wait`,
|
package/docs/README.zh.md
CHANGED
|
@@ -61,7 +61,7 @@ NIM,并把普通 Claude Code 参数原样传递。
|
|
|
61
61
|
|
|
62
62
|
Credits: One Ciel LLC
|
|
63
63
|
|
|
64
|
-
当前版本: `0.1.
|
|
64
|
+
当前版本: `0.1.71`
|
|
65
65
|
|
|
66
66
|
## 为什么存在
|
|
67
67
|
|
|
@@ -351,6 +351,20 @@ Hermes 格式模型或部分较旧的 Qwen tool template。
|
|
|
351
351
|
|
|
352
352
|
## 更新日志
|
|
353
353
|
|
|
354
|
+
### 0.1.71
|
|
355
|
+
|
|
356
|
+
- **MCP SSE channel 初始化**:channel bridge 收到 MCP `endpoint` 事件后会自动发送
|
|
357
|
+
`initialize` 与 `notifications/initialized`,让 AI-Net 风格的 push
|
|
358
|
+
notification 可以流入 Claude Any。
|
|
359
|
+
- **Channel SSE 诊断信息**:connector status 现在会显示 MCP endpoint、初始化状态
|
|
360
|
+
以及最后一次 MCP 初始化错误。
|
|
361
|
+
|
|
362
|
+
### 0.1.70
|
|
363
|
+
|
|
364
|
+
- **Linux 菜单调试日志修复**:key-debug 日志现在写入用户的 Claude Any config
|
|
365
|
+
目录,而不是全局 `/tmp`,避免受限 Linux 环境中的 permission crash。
|
|
366
|
+
- **best-effort key logging**:即使可选 key-debug 日志无法写入,菜单输入也不会失败。
|
|
367
|
+
|
|
354
368
|
### 0.1.69
|
|
355
369
|
|
|
356
370
|
- **实时 channel bridge**:新增 `/ca/channel/messages`、`/ca/channel/wait`、
|
package/docs/manual.md
CHANGED
package/package.json
CHANGED