Skip to content

Commit 0319c1d

Browse files
fix: strip CRLF from cowork-plugin-shim.sh during staging (#499) (#505)
* fix: strip CRLF from cowork-plugin-shim.sh during staging The shim originates from the upstream Windows .exe extract and ships with CRLF line endings. Bash fails to exec a script with CRLF shebangs/commands ("$'\r': command not found", syntax errors at function braces), so on Nix where the installed file is read-only the Claude Code subprocess crashes immediately and every cowork session reports `process_crashed`. Debian/AppImage installs inherit the same CRLF file but bite less often because the shim is only invoked once cowork is actively used. Normalise at the single staging point both the deb and Nix paths read from. The conversion is a no-op on LF-only input, so if upstream ever switches to LF this patch remains safe. Fixes #499 Reported-by: @olafkfreund Co-Authored-By: Claude <claude@anthropic.com> * test: use read builtin instead of sha256sum | awk Style guide prefers parameter expansion / bash builtins over forking awk. Same ground covered: the first whitespace-separated field of sha256sum output is captured. Co-Authored-By: Claude <claude@anthropic.com> --------- Co-authored-by: Claude <claude@anthropic.com>
1 parent eb90be3 commit 0319c1d

2 files changed

Lines changed: 111 additions & 3 deletions

File tree

scripts/staging/cowork-resources.sh

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,15 @@ copy_cowork_resources() {
1414

1515
local resources_src="$claude_extract_dir/lib/net45/resources"
1616

17-
# Copy cowork-plugin-shim.sh (used by app for MCP plugin sandboxing)
17+
# Copy cowork-plugin-shim.sh (used by app for MCP plugin sandboxing).
18+
# The upstream file ships from the Windows .exe extract with CRLF line
19+
# endings; bash exec fails on CRLF shebangs and command lines (issue #499).
1820
local shim_src="$resources_src/cowork-plugin-shim.sh"
21+
local shim_dest="$electron_resources_dest/cowork-plugin-shim.sh"
1922
if [[ -f $shim_src ]]; then
20-
cp "$shim_src" "$electron_resources_dest/cowork-plugin-shim.sh"
21-
chmod +x "$electron_resources_dest/cowork-plugin-shim.sh"
23+
cp "$shim_src" "$shim_dest"
24+
sed -i 's/\r$//' "$shim_dest"
25+
chmod +x "$shim_dest"
2226
echo "Copied cowork-plugin-shim.sh"
2327
else
2428
echo "Warning: cowork-plugin-shim.sh not found at $shim_src"
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
#!/usr/bin/env bats
2+
#
3+
# cowork-resources-staging.bats
4+
# Tests for scripts/staging/cowork-resources.sh — specifically that the
5+
# cowork-plugin-shim.sh staged into place has LF line endings regardless
6+
# of what the upstream Windows .exe extract shipped (issue #499).
7+
#
8+
9+
SCRIPT_DIR="$(cd "$(dirname "${BATS_TEST_FILENAME}")" && pwd)"
10+
11+
setup() {
12+
TEST_TMP=$(mktemp -d)
13+
export TEST_TMP
14+
15+
# Fake the staging layout the script expects.
16+
claude_extract_dir="$TEST_TMP/extract"
17+
electron_resources_dest="$TEST_TMP/dest"
18+
architecture='x64'
19+
mkdir -p "$claude_extract_dir/lib/net45/resources" \
20+
"$electron_resources_dest"
21+
22+
# shellcheck source=scripts/_common.sh
23+
source "$SCRIPT_DIR/../scripts/_common.sh"
24+
# shellcheck source=scripts/staging/cowork-resources.sh
25+
source "$SCRIPT_DIR/../scripts/staging/cowork-resources.sh"
26+
}
27+
28+
teardown() {
29+
if [[ -n "$TEST_TMP" && -d "$TEST_TMP" ]]; then
30+
rm -rf "$TEST_TMP"
31+
fi
32+
}
33+
34+
# Build a shim file at the upstream extract location with the requested
35+
# line-ending style. Contents are a minimal valid bash script.
36+
write_shim_src() {
37+
local style="$1"
38+
local shim="$claude_extract_dir/lib/net45/resources/cowork-plugin-shim.sh"
39+
printf '#!/bin/bash\n# Cowork plugin shim.\ncowork_require_token() {\n\treturn 0\n}\n' \
40+
> "$shim"
41+
if [[ $style == 'crlf' ]]; then
42+
sed -i 's/$/\r/' "$shim"
43+
fi
44+
}
45+
46+
# =============================================================================
47+
# CRLF normalization (issue #499)
48+
# =============================================================================
49+
50+
@test "copy_cowork_resources: strips CRLF from Windows-encoded shim" {
51+
write_shim_src crlf
52+
53+
# Sanity: source really is CRLF before we run the staging step.
54+
run grep -c $'\r$' \
55+
"$claude_extract_dir/lib/net45/resources/cowork-plugin-shim.sh"
56+
[[ "$status" -eq 0 ]]
57+
[[ "$output" -gt 0 ]]
58+
59+
run copy_cowork_resources
60+
[[ "$status" -eq 0 ]]
61+
62+
local dest="$electron_resources_dest/cowork-plugin-shim.sh"
63+
[[ -f "$dest" ]]
64+
65+
# No carriage returns survive into the staged copy.
66+
run grep -c $'\r' "$dest"
67+
[[ "$status" -eq 1 ]]
68+
}
69+
70+
@test "copy_cowork_resources: staged shim is executable and bash-parsable" {
71+
write_shim_src crlf
72+
73+
run copy_cowork_resources
74+
[[ "$status" -eq 0 ]]
75+
76+
local dest="$electron_resources_dest/cowork-plugin-shim.sh"
77+
[[ -x "$dest" ]]
78+
79+
# bash -n would have failed on CRLF ($'\r': command not found).
80+
run bash -n "$dest"
81+
[[ "$status" -eq 0 ]]
82+
}
83+
84+
@test "copy_cowork_resources: LF-only shim passes through unchanged" {
85+
write_shim_src lf
86+
87+
local src="$claude_extract_dir/lib/net45/resources/cowork-plugin-shim.sh"
88+
local src_sum _
89+
read -r src_sum _ < <(sha256sum "$src")
90+
91+
run copy_cowork_resources
92+
[[ "$status" -eq 0 ]]
93+
94+
local dest="$electron_resources_dest/cowork-plugin-shim.sh"
95+
local dest_sum
96+
read -r dest_sum _ < <(sha256sum "$dest")
97+
[[ "$src_sum" == "$dest_sum" ]]
98+
}
99+
100+
@test "copy_cowork_resources: missing shim emits warning without failing" {
101+
run copy_cowork_resources
102+
[[ "$status" -eq 0 ]]
103+
[[ "$output" == *'cowork-plugin-shim.sh not found'* ]]
104+
}

0 commit comments

Comments
 (0)