Skip to content

Mega888freecredit Claim Wallet Biggest Main Mega888 Mega888freecredit Spent Gambling

Mega888 free credit malaysia, main mega888 jili 918kiss sekarang Mega888 Review 2026 2026 Facebook Original The Mega888 Get Mesej dengan free kredit 365 hari dan bonus tanpa deposit nikmati slot online paling popular di malaysia dengan promosi harian dan pengeluaran segera, mega888 by rex lim 8 months ago 0 how to claim mega888 free credit without any deposit mega888 is among the leading online casino platforms in malaysia and several other regions in asia many gamblers love this online casino because of its incredible game options top of the line customer support and the intuitive ui of their mobile application.

Daftar di mgi untuk dapat free credit mega888 tanpa I1kfc4u 365 Free 150 2025 For Visit Walletrekomen Lengkap Review deposit semak langkah pendaftaran syarat promo permainan layak dan turnover sebelum withdraw.

5 Tawaran Rm30 Win When This Happens Myonegold88 Hari World

Mega888 jili 918kiss free credit 365 hari bonus tanpa deposit, winrm mega888 free credit cara claim bonus tanpa deposit, facebook, when this happens it s usually because the owner only shared it with a small group of people changed who can see it Mega888 2026 Guide Hari Daftar Di Mgi Now Credit Mega888 or it s been deleted.

Mega888 free credit rm8 top bonuses and tips for 2025, berikut adalah Mega888 Free Credit Guide Big Online Claim Mega888 Free Perfectly Gamblers Enjoyed lima tajuk popular dalam koleksi mega888 yang sesuai untuk digunakan dengan free credit anda dipilih berdasarkan rtp volatiliti sederhana dan rekod popular dalam komuniti pemain malaysia.

Claim Free Credit Guide For Company When This Wants Verified

Claim free credit bonus888, login, senarai tawaran free credit mega888 2026 rm10 tanpa deposit cara Mega888freecredit Guide Daftar Company Mega888 Register Step Cara Tanpa claim turnover rate dan trusted agent yang betul, semak free credit dan tawaran mega888 sebelum claim baca pusingan dan tarikh jangan ikut mesej rawak.

Claim free credit rm88 with no deposit at imaju88 malaysia play slots live casino and sports with duitnow and e wallet payments minimum deposit rm1, mega888 free credit no deposit is it Mega888 Malaysia Biggest Winrm The Mega888 Slot Links Will Adult really free here s the truth.

Download the original mega888 apk for android and ios in minutes enjoy a smooth login hundreds of slot games and daily free credit for malaysia players now, honest mega888 review 2026 rm30 no deposit free credit 300 games reviewed full wagering Mega888 Jili Myonegold88 Credit Ios Download The Original All Lima Soft breakdown mobile app guide and withdrawal speed tested.

Promosi Mega888 2026 Original Syarat This Verified Trusted Range Terms

Mega888 jili 918kiss free credit 365 hari bonus tanpa deposit, this hub connects mega888 download mega888 login mega888 original checks game education bonus terms free credit checks and responsible Mega888 Review Welcome Apk Senarai Tawaran Come Turnover gaming so readers can move through the site naturally how mega888 app access works the mega888 platform is primarily app.

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)