multi-claude-cli 0.12.0__tar.gz → 0.12.1__tar.gz
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.
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/PKG-INFO +1 -1
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/__init__.py +1 -1
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/refresh.py +15 -2
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude_cli.egg-info/PKG-INFO +1 -1
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_refresh.py +33 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/LICENSE +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/README.md +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/pyproject.toml +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/setup.cfg +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/__main__.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/accounts.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/actions.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/artifacts.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/bundle.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/cli.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/completion.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/config.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/doctor.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/env.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/fsutil.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/history.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/hook.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/identity.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/launcher.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/lock.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/migrate.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/plan.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/platform.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/restore.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/routes.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/sessions.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/shared.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/shellpath.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/statusline.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude/usage.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude_cli.egg-info/SOURCES.txt +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude_cli.egg-info/dependency_links.txt +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude_cli.egg-info/entry_points.txt +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude_cli.egg-info/top_level.txt +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_accounts.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_artifacts.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_bundle.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_completion.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_config.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_doctor.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_everyday.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_fsutil.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_handoff.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_help.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_history.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_identity.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_install.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_mcp.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_migrate.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_onboarding.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_packaging.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_plan.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_platform.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_rename.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_routes.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_sessions.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_set.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_shared_exclude.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_statusline.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_usability.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_usage.py +0 -0
- {multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/tests/test_vscode.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: multi-claude-cli
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.1
|
|
4
4
|
Summary: Run several Claude Code accounts side by side: separate CLAUDE_CONFIG_DIR directories, launchers, proxies, environment variables and arguments.
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/jakoes-wu/multi-claude
|
|
@@ -230,13 +230,25 @@ class _Lock(object):
|
|
|
230
230
|
"""自己建的一把锁,以及用来确认“这个路径上还是我建的那个目录”的指纹。
|
|
231
231
|
|
|
232
232
|
指纹是设备号、inode 和自己最后一次设置的修改时间(proper-lockfile 也靠修改时间判断锁有没有被别人动过)。
|
|
233
|
-
只比 inode
|
|
233
|
+
只比 inode 不够:别人删掉重建时文件系统可能复用同一个编号。修改时间也不能交给内核取:
|
|
234
|
+
ext4 上几毫秒内删掉重建的目录,inode 和内核记的修改时间多半与原来相同(实测 2000 次里 1993 次),会把别人的锁认成自己的。
|
|
235
|
+
所以建锁和保活时都用 _set_mtime_now 写入此刻的精细时间。
|
|
236
|
+
不能改成往锁目录里放标记文件:proper-lockfile 用 rmdir 清理过期锁,目录非空就删不掉,锁会一直卡住。
|
|
234
237
|
"""
|
|
235
238
|
|
|
236
239
|
def __init__(self, path: str) -> None:
|
|
237
240
|
self.path = path
|
|
241
|
+
try:
|
|
242
|
+
self._set_mtime_now()
|
|
243
|
+
except OSError:
|
|
244
|
+
# 设不了时间就用内核写的时间记指纹:锁照样能用,只是分辨不出同一时钟节拍里的删掉重建。
|
|
245
|
+
pass
|
|
238
246
|
self._stamp = self._read()
|
|
239
247
|
|
|
248
|
+
def _set_mtime_now(self) -> None:
|
|
249
|
+
now_ns = time.time_ns()
|
|
250
|
+
os.utime(self.path, ns=(now_ns, now_ns))
|
|
251
|
+
|
|
240
252
|
def _read(self) -> Optional[Tuple[int, int, int]]:
|
|
241
253
|
try:
|
|
242
254
|
status = os.lstat(self.path)
|
|
@@ -252,7 +264,8 @@ class _Lock(object):
|
|
|
252
264
|
if not self.is_mine():
|
|
253
265
|
return
|
|
254
266
|
try:
|
|
255
|
-
os.utime(
|
|
267
|
+
# 不能用 os.utime(path, None):那样又由内核取时间,保活一次后指纹就退回分辨不出重建的状态。
|
|
268
|
+
self._set_mtime_now()
|
|
256
269
|
except OSError:
|
|
257
270
|
return
|
|
258
271
|
fresh = self._read()
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: multi-claude-cli
|
|
3
|
-
Version: 0.12.
|
|
3
|
+
Version: 0.12.1
|
|
4
4
|
Summary: Run several Claude Code accounts side by side: separate CLAUDE_CONFIG_DIR directories, launchers, proxies, environment variables and arguments.
|
|
5
5
|
License: MIT
|
|
6
6
|
Project-URL: Homepage, https://github.com/jakoes-wu/multi-claude
|
|
@@ -7,13 +7,16 @@
|
|
|
7
7
|
|
|
8
8
|
import json
|
|
9
9
|
import os
|
|
10
|
+
import shutil
|
|
10
11
|
import signal
|
|
11
12
|
import stat
|
|
12
13
|
import subprocess
|
|
13
14
|
import sys
|
|
15
|
+
import tempfile
|
|
14
16
|
import threading
|
|
15
17
|
import time
|
|
16
18
|
import types
|
|
19
|
+
import unittest
|
|
17
20
|
from http.server import BaseHTTPRequestHandler
|
|
18
21
|
from unittest import mock
|
|
19
22
|
|
|
@@ -482,6 +485,36 @@ class RenewTest(RefreshTestCase):
|
|
|
482
485
|
self.assertNotIn("add-generic-password", call)
|
|
483
486
|
|
|
484
487
|
|
|
488
|
+
class LockStampTest(unittest.TestCase):
|
|
489
|
+
"""锁指纹里的修改时间必须是本工具写入的精细时间,不能交给内核取。
|
|
490
|
+
|
|
491
|
+
ext4 上几毫秒内删掉重建的锁目录,inode 和内核记的修改时间都与原来相同;只有自己写入的时间能分辨出来。
|
|
492
|
+
这里不依赖 inode 复用,在任何文件系统上确定性地检查“建锁和保活都写入了 time_ns 的值”。
|
|
493
|
+
"""
|
|
494
|
+
|
|
495
|
+
# 低位非零:内核按时钟节拍记的时间不会恰好是这个值
|
|
496
|
+
STAMP_NS = 1_700_000_000_123_456_789
|
|
497
|
+
|
|
498
|
+
def setUp(self):
|
|
499
|
+
self.tmp = tempfile.mkdtemp(prefix="mcl-lock-")
|
|
500
|
+
self.addCleanup(shutil.rmtree, self.tmp, True)
|
|
501
|
+
self.path = os.path.join(self.tmp, refresh.LOCK_NAME)
|
|
502
|
+
os.mkdir(self.path)
|
|
503
|
+
|
|
504
|
+
def test_creating_and_touching_write_the_fine_clock(self):
|
|
505
|
+
# refresh.time 就是标准库 time:桩只包住这两次调用,不影响进程里的其它代码
|
|
506
|
+
with mock.patch.object(refresh.time, "time_ns", return_value=self.STAMP_NS):
|
|
507
|
+
lock = refresh._Lock(self.path)
|
|
508
|
+
self.assertEqual(os.lstat(self.path).st_mtime_ns, self.STAMP_NS)
|
|
509
|
+
self.assertTrue(lock.is_mine())
|
|
510
|
+
|
|
511
|
+
later = self.STAMP_NS + 5_000_000_000
|
|
512
|
+
with mock.patch.object(refresh.time, "time_ns", return_value=later):
|
|
513
|
+
lock.touch()
|
|
514
|
+
self.assertEqual(os.lstat(self.path).st_mtime_ns, later)
|
|
515
|
+
self.assertTrue(lock.is_mine())
|
|
516
|
+
|
|
517
|
+
|
|
485
518
|
class KeychainWriteTest(CliTestCase):
|
|
486
519
|
"""macOS 分支:凭据在钥匙串里,写回走 `security -i`;用假 security,任何平台都能跑。"""
|
|
487
520
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude_cli.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude_cli.egg-info/entry_points.txt
RENAMED
|
File without changes
|
{multi_claude_cli-0.12.0 → multi_claude_cli-0.12.1}/src/multi_claude_cli.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|