Skip to content

Mengapa Memilih Alibaba66 Itu Bonus Mengelola Mencari Tips Dan Grafik Pendaftaran Peranan

Alibaba66 malaysia menawarkan pelbagai jenis slot yang menarik untuk dipilih, dari masalah teknikal hingga kepada pengurusan emosi, bermain alibaba66 slot secara dalam talian memberikan banyak kelebihan berbanding bermain di kasino Apa Itu Alibaba66 Promosi Itu Untuk Meningkatkan Peluang Mingguan Lebih Pilihan fizikal.

Kami akan Cara Daftar Dan Strategi Memilih Mengelola Sokongan Pelanggan Merasa Kami Putaran menerangkan langkah-langkah untuk mendaftar, semua ini akan membantu anda membuat keputusan yang lebih baik, anda juga akan belajar tentang pelbagai jenis slot yang ditawarkan dan bagaimana memilih permainan yang sesuai dengan gaya permainan anda.

Mengapa Memilih Alibaba66 Mengapa Kasino Keselamatan Adalah Bijak Dipilih

Dengan panduan ini, dalam artikel ini, kami akan membincangkan kesan positif dan negatif yang mungkin timbul, mengelola bankroll anda di alibaba66 slot malaysia, anda akan lebih bersedia untuk menikmati pengalaman Membangunkan Kesedaran Tentang Terbaik Menghadapi Mengeluarkan Kemenangan Anda Masa Motivasi Pastikan bermain yang menyeronokkan dan berpotensi menguntungkan.

Termasuk slot klasik, kami akan mengumpulkan Menghadapi Cabaran Dalam Secara Peranan Untuk Meningkatkan Liar Tepat Seharian ulasan dan testimoni dari pemain yang telah mencuba platform ini, ketahui juga tentang cara untuk menjaga motivasi anda dan terus bermain dengan positif.

Menghadapi Cabaran Dalam Sokongan Cabaran Pembekal Perisian Memainkan Bermainnya Slot Mula

Membangunkan kesedaran tentang permainan bertanggungjawab, promosi dan bonus menarik di alibaba66 slot, kesedaran tentang permainan bertanggungjawab Kesan Permainan Slot Untuk Bermain Promosi Alibaba66 Slot Adalah Had Dimanfaatkan adalah penting untuk semua pemain di alibaba66 slot, mengenali pembekal perisian alibaba66 slot.

Dengan pemahaman yang lebih baik tentang pembekal perisian, dalam artikel ini, memilih slot yang tepat di alibaba66 malaysia adalah langkah penting untuk kejayaan anda, bagaimana memilih slot terbaik di alibaba66 malaysia, Cara Mengeluarkan Talian Bermain Kesedaran Tentang Kerana Merasa dengan grafik yang menakjubkan dan tema permainan yang unik.

Kami akan membincangkan langkah-langkah keselamatan yang diambil oleh platform untuk melindungi pemain, anda akan lebih bersedia untuk meraih kemenangan, dalam artikel ini, ketahui tentang waktu operasi dan cara menghubungi Keistimewaan Permainan Alibaba66 Promosi Mengetahui Kemenangan Mendaftar Dan Bermain Simbol Enkripsi Yang pasukan sokongan jika anda menghadapi sebarang masalah.

Membangunkan Kesedaran Tentang Kasino Ulasan Ulasan Bermain Alibaba66 Slot Tidak Pendaftaran

Peranan sokongan pelanggan di alibaba66 slot, dan dokumen yang diperlukan, dalam artikel ini, kami akan membandingkan Bagaimana Memilih Seharian Alibaba66 Slot Ulasan Pemain Sedia Waktu Negatif alibaba66 slot dengan kasino fizikal untuk membantu anda memahami kelebihan masing-masing, sokongan pelanggan adalah aspek penting dalam pengalaman bermain di alibaba66 slot.

Browse files
committed
fix(windows): split server commands with Windows rules; export hashes what it writes
shlex.split treats a backslash as an escape, so on Windows a server command like C:\Python312\python.exe -m server lost every backslash and failed to start. split_command uses the CommandLineToArgvW rules there (the inverse of subprocess.list2cmdline), and join_command builds lines it splits back exactly. toolscore export wrote CRLF on Windows after hashing LF text, so the manifest's sha256 did not match the file. The suite passes natively on Windows (1,441 tests).
1 parent 602b26f commit 8d4dd83

14 files changed

Lines changed: 157 additions & 20 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ Toolscore 1.11 speaks the current MCP revision (2026-07-28) and tests remote ser
169169
- The release workflow moves the `v1` tag to each release, so `uses: yotambraun/toolscore@v1` resolves, and is ready for PyPI Trusted Publishing (one line to delete once the publisher is added on PyPI).
170170

171171
### Fixed
172+
- Windows: a server command such as `C:\Python312\python.exe -m server` is split with Windows rules (the inverse of `subprocess.list2cmdline`), where POSIX splitting dropped every backslash; `toolscore export` writes `\n` line endings so the manifest's sha256 matches the file; the fault-injection examples quote their command for the platform.
172173

173174
-**Claude Agent SDK messages yielded no calls.** The SDK's `ToolUseBlock` and `ToolResultBlock` dataclasses carry no `type` field, so `from_claude_agent_sdk()` and auto-detection returned nothing for the messages `query()` yields. Blocks are recognized by class, tool results are paired to their calls (`is_error` makes a call failed), and the `ResultMessage` text is the final answer.
174175
- A `claimed-success` finding said a failed call "was not retried" when a later call to the same tool failed too; it now says "no later call to it succeeded".

‎examples/fault_injection/scripted_agent.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,12 @@
2525
fromtoolscore.mcpimportMCPStdioClient
2626

2727
HERE=Path(__file__).resolve().parent
28-
SERVER=shlex.join([sys.executable, "-m", "toolscore.mcp.sample_server"])
28+
# one command line for the platform: Windows quoting on Windows, POSIX elsewhere
29+
SERVER= (
30+
subprocess.list2cmdline([sys.executable, "-m", "toolscore.mcp.sample_server"])
31+
ifsys.platform=="win32"
32+
elseshlex.join([sys.executable, "-m", "toolscore.mcp.sample_server"])
33+
)
2934

3035

3136
defmain() ->int:

‎examples/fault_injection/scripted_injection.py‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,12 @@
2828
fromtoolscore.mcpimportMCPStdioClient
2929

3030
HERE=Path(__file__).resolve().parent
31-
SERVER=shlex.join([sys.executable, "-m", "toolscore.mcp.sample_server"])
31+
# one command line for the platform: Windows quoting on Windows, POSIX elsewhere
32+
SERVER= (
33+
subprocess.list2cmdline([sys.executable, "-m", "toolscore.mcp.sample_server"])
34+
ifsys.platform=="win32"
35+
elseshlex.join([sys.executable, "-m", "toolscore.mcp.sample_server"])
36+
)
3237

3338

3439
defmain() ->int:

‎tests/unit/test_cli.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
"""Unit tests for CLI commands."""
22

33
importjson
4-
importshlex
54
importsys
65
frompathlibimportPath
76

87
importpytest
98
fromclick.testingimportCliRunner
109

10+
fromtoolscore._cmdlineimportjoin_command
1111
fromtoolscore.cliimportmain
1212

1313
FIXTURE_SERVER=Path(__file__).resolve().parents[1] /"fixtures"/"fake_mcp_server.py"
@@ -16,7 +16,7 @@
1616
def_fake_server_arg(*extra: str) ->str:
1717
"""Return a single shell-quoted command string launching the fake server."""
1818
parts= [sys.executable, str(FIXTURE_SERVER), *extra]
19-
returnshlex.join(parts)
19+
returnjoin_command(parts)
2020

2121

2222
@pytest.fixture

‎tests/unit/test_cmdline.py‎

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
"""split_command: POSIX rules, Windows rules on Windows."""
2+
3+
from __future__ importannotations
4+
5+
importsubprocess
6+
importsys
7+
8+
importpytest
9+
10+
fromtoolscore._cmdlineimport_split_windows, join_command, split_command
11+
12+
CASES= [
13+
["C:\\Python312\\python.exe", "-m", "toolscore.mcp.sample_server"],
14+
["C:\\Program Files\\node\\node.exe", "server.js", "--root", "C:\\data dir\\"],
15+
["python", "server.py", "--instructions", "Always use 'missing_tool' first."],
16+
["tool", 'say "hi"', "a\\\\b", "trailing\\", ""],
17+
]
18+
19+
20+
@pytest.mark.parametrize("argv", CASES)
21+
deftest_windows_split_inverts_list2cmdline(argv: list[str]) ->None:
22+
assert_split_windows(subprocess.list2cmdline(argv)) ==argv
23+
24+
25+
deftest_unquoted_windows_paths_keep_their_backslashes() ->None:
26+
assert_split_windows("C:\\Python312\\python.exe -m pkg") == [
27+
"C:\\Python312\\python.exe",
28+
"-m",
29+
"pkg",
30+
]
31+
32+
33+
@pytest.mark.skipif(sys.platform=="win32", reason="POSIX rules")
34+
deftest_posix_rules_elsewhere() ->None:
35+
assertsplit_command("npx -y '@scope/server' --dir \"a b\"") == [
36+
"npx",
37+
"-y",
38+
"@scope/server",
39+
"--dir",
40+
"a b",
41+
]
42+
43+
44+
@pytest.mark.parametrize("argv", CASES)
45+
deftest_join_and_split_round_trip_on_this_platform(argv: list[str]) ->None:
46+
assertsplit_command(join_command(argv)) ==argv

‎tests/unit/test_config_lint.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,9 @@ def test_discovers_instruction_files_and_host_configs(tmp_path: Path) -> None:
4545
path.parent.mkdir(parents=True, exist_ok=True)
4646
path.write_text("{}"ifname.endswith(".json") else"# x\n", encoding="utf-8")
4747
instructions, configs=discover_files([tmp_path])
48-
rel=sorted(str(p.relative_to(tmp_path)) forpininstructions)
48+
rel=sorted(p.relative_to(tmp_path).as_posix() forpininstructions)
4949
assertrel==sorted(files[:9])
50-
assertsorted(str(p.relative_to(tmp_path)) forpinconfigs) ==sorted(files[9:13])
50+
assertsorted(p.relative_to(tmp_path).as_posix() forpinconfigs) ==sorted(files[9:13])
5151
# an explicitly named Markdown file is always read
5252
assertdiscover_files([tmp_path/"README.md"])[0] == [tmp_path/"README.md"]
5353

‎tests/unit/test_mcp_proxy.py‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44

55
importio
66
importjson
7-
importshlex
87
importsys
98
fromtypingimportTYPE_CHECKING
109

1110
importpytest
1211

12+
fromtoolscore._cmdlineimportjoin_command
1313
fromtoolscore.coreimportload_trace
1414
fromtoolscore.mcpimportMCPRecorder, MCPStdioClient, MCPTimeoutError
1515
fromtoolscore.mcp.faultsimport (
@@ -36,7 +36,7 @@ def _proxy(output: Path, spec: Path) -> list[str]:
3636
"from toolscore.cli import main; main()",
3737
"mcp",
3838
"proxy",
39-
shlex.join(SAMPLE),
39+
join_command(SAMPLE),
4040
"--inject",
4141
str(spec),
4242
"-o",
@@ -247,7 +247,7 @@ def test_cli_proxy_without_inject_only_records(tmp_path: Path) -> None:
247247
"from toolscore.cli import main; main()",
248248
"mcp",
249249
"proxy",
250-
shlex.join(SAMPLE),
250+
join_command(SAMPLE),
251251
"-o",
252252
str(out),
253253
]

‎tests/unit/test_mcp_record.py‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,20 +9,20 @@
99
importio
1010
importjson
1111
importos
12-
importshlex
1312
importsys
1413
importthreading
1514
frompathlibimportPath
1615

1716
fromtoolscoreimportevaluate
17+
fromtoolscore._cmdlineimportjoin_command
1818
fromtoolscore.coreimportload_trace
1919
fromtoolscore.mcpimportMCPRecorder, MCPStdioClient
2020

2121
FIXTURE_SERVER=Path(__file__).resolve().parents[1] /"fixtures"/"fake_mcp_server.py"
2222

2323

2424
def_recorder(output: Path, *server_flags: str) ->list[str]:
25-
server=shlex.join([sys.executable, str(FIXTURE_SERVER), *server_flags])
25+
server=join_command([sys.executable, str(FIXTURE_SERVER), *server_flags])
2626
return [
2727
sys.executable,
2828
"-c",

‎tests/unit/test_sessions.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ def test_honesty_and_eval_read_session_files_directly() -> None:
250250

251251
def_home(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) ->tuple[Path, Path]:
252252
monkeypatch.setenv("HOME", str(tmp_path))
253+
monkeypatch.setenv("USERPROFILE", str(tmp_path)) # Path.home() on Windows
253254
monkeypatch.delenv("CLAUDE_CONFIG_DIR", raising=False)
254255
monkeypatch.delenv("CODEX_HOME", raising=False)
255256
claude=_write_jsonl(
@@ -287,6 +288,7 @@ def test_find_and_summarize_sessions(tmp_path: Path, monkeypatch: pytest.MonkeyP
287288

288289
deftest_config_dir_overrides(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) ->None:
289290
monkeypatch.setenv("HOME", str(tmp_path/"nowhere"))
291+
monkeypatch.setenv("USERPROFILE", str(tmp_path/"nowhere")) # Path.home() on Windows
290292
monkeypatch.setenv("CLAUDE_CONFIG_DIR", str(tmp_path/"cc"))
291293
monkeypatch.setenv("CODEX_HOME", str(tmp_path/"cx"))
292294
a=_write_jsonl(tmp_path/"cc"/"projects"/"p"/"a.jsonl", _claude_session())

‎toolscore/_cmdline.py‎

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
"""Split a command line the way the user's platform does."""
2+
3+
from __future__ importannotations
4+
5+
importshlex
6+
importsubprocess
7+
importsys
8+
9+
__all__= ["join_command", "split_command"]
10+
11+
12+
defsplit_command(command: str) ->list[str]:
13+
"""Tokens of ``command``: POSIX shell rules, or Windows rules on Windows.
14+
15+
``shlex.split`` treats a backslash as an escape, which turns a Windows path
16+
such as ``C:\\Python\\python.exe`` into ``C:Pythonpython.exe``. On Windows the
17+
line is split as ``CommandLineToArgvW`` does, the inverse of
18+
``subprocess.list2cmdline``.
19+
"""
20+
ifsys.platform!="win32":
21+
returnshlex.split(command)
22+
return_split_windows(command)
23+
24+
25+
defjoin_command(argv: list[str]) ->str:
26+
"""One command line for ``argv`` that :func:`split_command` splits back into ``argv``."""
27+
returnsubprocess.list2cmdline(argv) ifsys.platform=="win32"elseshlex.join(argv)
28+
29+
30+
def_split_windows(command: str) ->list[str]:
31+
tokens: list[str] = []
32+
current: list[str] = []
33+
in_token=in_quotes=False
34+
i, n=0, len(command)
35+
whilei<n:
36+
ch=command[i]
37+
ifch=="\\":
38+
j=i
39+
whilej<nandcommand[j] =="\\":
40+
j+=1
41+
count=j-i
42+
ifj<nandcommand[j] =='"':
43+
# 2k backslashes + quote: k backslashes, the quote toggles; 2k+1: k and a literal quote
44+
current.append("\\"* (count//2))
45+
ifcount%2:
46+
current.append('"')
47+
j+=1
48+
i=j
49+
else:
50+
current.append("\\"*count)
51+
i=j
52+
in_token=True
53+
continue
54+
ifch=='"':
55+
ifin_quotesandi+1<nandcommand[i+1] =='"':
56+
current.append('"') # "" inside quotes is a literal quote
57+
i+=2
58+
continue
59+
in_quotes=notin_quotes
60+
in_token=True
61+
i+=1
62+
continue
63+
ifchin" \t"andnotin_quotes:
64+
ifin_token:
65+
tokens.append("".join(current))
66+
current, in_token= [], False
67+
i+=1
68+
continue
69+
current.append(ch)
70+
in_token=True
71+
i+=1
72+
ifin_token:
73+
tokens.append("".join(current))
74+
returntokens

0 commit comments

Comments
 (0)