remote-claude 0.2.10 → 0.2.12
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/.env.example +4 -0
- package/README.md +8 -2
- package/bin/cdx +20 -0
- package/bin/cx +20 -0
- package/bin/remote-claude +12 -2
- package/client/__init__.py +3 -0
- package/init.sh +31 -2
- package/lark_client/card_builder.py +14 -7
- package/lark_client/config.py +10 -0
- package/lark_client/main.py +52 -8
- package/lark_client/session_bridge.py +1 -1
- package/package.json +2 -1
- package/remote_claude.py +36 -11
- package/server/__init__.py +1 -0
- package/server/parsers/__init__.py +13 -0
- package/server/parsers/base_parser.py +44 -0
- package/server/parsers/claude_parser.py +1263 -0
- package/server/parsers/codex_parser.py +1496 -0
- package/server/server.py +44 -8
- package/server/shared_state.py +3 -2
- package/utils/__init__.py +3 -0
- package/utils/session.py +23 -2
package/server/server.py
CHANGED
|
@@ -698,6 +698,9 @@ class ProxyServer:
|
|
|
698
698
|
# 启动 PTY 读取任务
|
|
699
699
|
asyncio.create_task(self._read_pty())
|
|
700
700
|
|
|
701
|
+
# 切换到运行阶段日志
|
|
702
|
+
self._switch_to_runtime_logging()
|
|
703
|
+
|
|
701
704
|
# 等待服务器关闭
|
|
702
705
|
async with self.server:
|
|
703
706
|
await self.server.serve_forever()
|
|
@@ -713,6 +716,32 @@ class ProxyServer:
|
|
|
713
716
|
return CodexParser()
|
|
714
717
|
return ClaudeParser()
|
|
715
718
|
|
|
719
|
+
def _switch_to_runtime_logging(self):
|
|
720
|
+
"""从启动日志切换到运行阶段日志"""
|
|
721
|
+
root_logger = logging.getLogger()
|
|
722
|
+
|
|
723
|
+
# 移除启动日志 handler(保留 stdout handler)
|
|
724
|
+
for handler in root_logger.handlers[:]:
|
|
725
|
+
if isinstance(handler, logging.FileHandler) and \
|
|
726
|
+
not hasattr(handler, '_runtime_handler') and \
|
|
727
|
+
not hasattr(handler, '_debug_handler'):
|
|
728
|
+
root_logger.removeHandler(handler)
|
|
729
|
+
|
|
730
|
+
# 添加运行阶段日志文件
|
|
731
|
+
safe_name = _safe_filename(self.session_name)
|
|
732
|
+
runtime_handler = logging.FileHandler(
|
|
733
|
+
f"{SOCKET_DIR}/{safe_name}_server.log",
|
|
734
|
+
encoding="utf-8"
|
|
735
|
+
)
|
|
736
|
+
runtime_handler.setFormatter(logging.Formatter(
|
|
737
|
+
"%(asctime)s.%(msecs)03d [%(name)s] %(levelname)s %(message)s",
|
|
738
|
+
datefmt="%Y-%m-%d %H:%M:%S"
|
|
739
|
+
))
|
|
740
|
+
runtime_handler._runtime_handler = True # 标记,方便后续清理
|
|
741
|
+
root_logger.addHandler(runtime_handler)
|
|
742
|
+
|
|
743
|
+
logger.info(f"日志已切换到运行阶段: {safe_name}_server.log")
|
|
744
|
+
|
|
716
745
|
def _get_effective_cmd(self) -> str:
|
|
717
746
|
"""根据 cli_type 返回实际执行的命令(codex 时使用 'codex',否则用 claude_cmd)"""
|
|
718
747
|
if self.cli_type == "codex":
|
|
@@ -998,20 +1027,27 @@ if __name__ == "__main__":
|
|
|
998
1027
|
help="debug 日志输出完整诊断信息(indicator、repr 等)")
|
|
999
1028
|
args = parser.parse_args()
|
|
1000
1029
|
|
|
1001
|
-
#
|
|
1002
|
-
from utils.session import USER_DATA_DIR
|
|
1030
|
+
# 配置日志:启动阶段输出到 stdout + startup.log
|
|
1031
|
+
from utils.session import USER_DATA_DIR, _safe_filename
|
|
1003
1032
|
USER_DATA_DIR.mkdir(parents=True, exist_ok=True)
|
|
1004
|
-
|
|
1033
|
+
|
|
1034
|
+
# 先配置基本输出(stdout)
|
|
1005
1035
|
logging.basicConfig(
|
|
1006
|
-
level=logging.
|
|
1036
|
+
level=logging.INFO,
|
|
1007
1037
|
format="%(asctime)s.%(msecs)03d [%(name)s] %(levelname)s %(message)s",
|
|
1008
1038
|
datefmt="%Y-%m-%d %H:%M:%S",
|
|
1009
|
-
handlers=[
|
|
1010
|
-
logging.FileHandler(_log_path, encoding="utf-8"),
|
|
1011
|
-
logging.StreamHandler(sys.stdout),
|
|
1012
|
-
],
|
|
1039
|
+
handlers=[logging.StreamHandler(sys.stdout)],
|
|
1013
1040
|
)
|
|
1014
1041
|
|
|
1042
|
+
# 添加启动日志 handler
|
|
1043
|
+
startup_handler = logging.FileHandler(USER_DATA_DIR / "startup.log", encoding="utf-8")
|
|
1044
|
+
startup_handler.setFormatter(logging.Formatter(
|
|
1045
|
+
"%(asctime)s.%(msecs)03d [%(name)s] %(levelname)s %(message)s",
|
|
1046
|
+
datefmt="%Y-%m-%d %H:%M:%S"
|
|
1047
|
+
))
|
|
1048
|
+
startup_handler._startup_handler = True # 标记为启动日志 handler
|
|
1049
|
+
logging.getLogger().addHandler(startup_handler)
|
|
1050
|
+
|
|
1015
1051
|
claude_cmd = os.environ.get("CLAUDE_COMMAND", "claude")
|
|
1016
1052
|
logger.info(f"CLAUDE_COMMAND={claude_cmd!r}")
|
|
1017
1053
|
run_server(args.session_name, args.claude_args, claude_cmd=claude_cmd,
|
package/server/shared_state.py
CHANGED
|
@@ -93,6 +93,7 @@ class SharedStateWriter:
|
|
|
93
93
|
"""写端:Server 进程持有,生命周期与 ProxyServer 相同"""
|
|
94
94
|
|
|
95
95
|
def __init__(self, session_name: str):
|
|
96
|
+
self._session_name = session_name
|
|
96
97
|
self._path = get_mq_path(session_name)
|
|
97
98
|
self._path.parent.mkdir(parents=True, exist_ok=True)
|
|
98
99
|
|
|
@@ -139,7 +140,7 @@ class SharedStateWriter:
|
|
|
139
140
|
"input_area_text": window.input_area_text,
|
|
140
141
|
"timestamp": window.timestamp,
|
|
141
142
|
"layout_mode": window.layout_mode,
|
|
142
|
-
"cli_type": getattr(window, "cli_type", "
|
|
143
|
+
"cli_type": getattr(window, "cli_type", "unknown"),
|
|
143
144
|
}
|
|
144
145
|
data = json.dumps(snapshot, ensure_ascii=False).encode('utf-8')
|
|
145
146
|
|
|
@@ -177,7 +178,7 @@ class SharedStateReader:
|
|
|
177
178
|
反映跨进程写入更新的问题。每次 read() 打开文件读取后关闭,保证读到最新数据。
|
|
178
179
|
"""
|
|
179
180
|
|
|
180
|
-
_EMPTY = {"blocks": [], "status_line": None, "bottom_bar": None, "option_block": None}
|
|
181
|
+
_EMPTY = {"blocks": [], "status_line": None, "bottom_bar": None, "option_block": None, "cli_type": "claude"}
|
|
181
182
|
|
|
182
183
|
def __init__(self, session_name: str):
|
|
183
184
|
self._path = get_mq_path(session_name)
|
package/utils/session.py
CHANGED
|
@@ -223,10 +223,30 @@ def list_active_sessions() -> List[dict]:
|
|
|
223
223
|
import datetime
|
|
224
224
|
try:
|
|
225
225
|
mtime = pid_file.stat().st_mtime
|
|
226
|
-
start_time = datetime.datetime.fromtimestamp(mtime).strftime("%H:%M")
|
|
226
|
+
start_time = datetime.datetime.fromtimestamp(mtime).strftime("%m-%d %H:%M")
|
|
227
227
|
except OSError:
|
|
228
228
|
mtime = 0
|
|
229
229
|
start_time = "?"
|
|
230
|
+
|
|
231
|
+
# 读取 .mq 文件获取 cli_type(避免循环导入,在函数内导入)
|
|
232
|
+
try:
|
|
233
|
+
import sys
|
|
234
|
+
from pathlib import Path
|
|
235
|
+
import logging
|
|
236
|
+
project_root = str(Path(__file__).parent.parent)
|
|
237
|
+
if project_root not in sys.path:
|
|
238
|
+
sys.path.insert(0, project_root)
|
|
239
|
+
from server.shared_state import SharedStateReader
|
|
240
|
+
reader = SharedStateReader(session_name)
|
|
241
|
+
snapshot = reader.read()
|
|
242
|
+
cli_type = snapshot.get("cli_type", "claude")
|
|
243
|
+
except Exception as e:
|
|
244
|
+
# 添加详细日志记录,便于诊断问题
|
|
245
|
+
import logging
|
|
246
|
+
logger = logging.getLogger('Session')
|
|
247
|
+
logger.warning(f"读取共享内存 cli_type 失败: session={session_name}, error={e}")
|
|
248
|
+
cli_type = "claude" # 读取失败时使用默认值
|
|
249
|
+
|
|
230
250
|
sessions.append({
|
|
231
251
|
"name": session_name,
|
|
232
252
|
"socket": str(sock_file),
|
|
@@ -234,7 +254,8 @@ def list_active_sessions() -> List[dict]:
|
|
|
234
254
|
"cwd": cwd or "",
|
|
235
255
|
"start_time": start_time,
|
|
236
256
|
"mtime": mtime,
|
|
237
|
-
"tmux": tmux_session_exists(session_name)
|
|
257
|
+
"tmux": tmux_session_exists(session_name),
|
|
258
|
+
"cli_type": cli_type
|
|
238
259
|
})
|
|
239
260
|
except (ProcessLookupError, ValueError, OSError):
|
|
240
261
|
# 进程不存在或文件被并发清理,清理残留文件
|