Fix/WA reconnect, container perms, assist name in env (#297)

* fix: WA 515 stream error reconnect exiting early before key sync

Pass isReconnect flag on 515 reconnect so the registered-creds check
doesn't bail out before the handshake completes (caused "logging in..."
hang after successful pairing).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: container permission errors on Docker with non-default uid

Make /home/node world-writable in the Dockerfile so the SDK can write
.claude.json. Add --user flag matching host uid/gid in container-runner
so bind-mounted files are accessible. Skip when running as root (uid 0),
as the container's node user (uid 1000), or on native Windows.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: write ASSISTANT_NAME to .env during setup

When a custom assistant name is chosen, persist it to .env so config.ts
picks it up at runtime. Uses temp file for cross-platform sed
compatibility (macOS/Linux/WSL).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Koshkoshinsk
2026-02-18 10:29:55 +02:00
committed by GitHub
parent f257b93d34
commit 802805d2ec
4 changed files with 23 additions and 4 deletions

View File

@@ -79,6 +79,15 @@ if [ "$ASSISTANT_NAME" != "Andy" ]; then
fi fi
done done
# Add ASSISTANT_NAME to .env so config.ts picks it up
ENV_FILE="$PROJECT_ROOT/.env"
if [ -f "$ENV_FILE" ] && grep -q '^ASSISTANT_NAME=' "$ENV_FILE"; then
sed "s/^ASSISTANT_NAME=.*/ASSISTANT_NAME=$ASSISTANT_NAME/" "$ENV_FILE" > "$ENV_FILE.tmp" && mv "$ENV_FILE.tmp" "$ENV_FILE"
else
echo "ASSISTANT_NAME=$ASSISTANT_NAME" >> "$ENV_FILE"
fi
log "Set ASSISTANT_NAME=$ASSISTANT_NAME in .env"
NAME_UPDATED="true" NAME_UPDATED="true"
fi fi

View File

@@ -56,7 +56,7 @@ RUN mkdir -p /workspace/group /workspace/global /workspace/extra /workspace/ipc/
RUN printf '#!/bin/bash\nset -e\ncd /app && npx tsc --outDir /tmp/dist 2>&1 >&2\nln -s /app/node_modules /tmp/dist/node_modules\nchmod -R a-w /tmp/dist\ncat > /tmp/input.json\nnode /tmp/dist/index.js < /tmp/input.json\n' > /app/entrypoint.sh && chmod +x /app/entrypoint.sh RUN printf '#!/bin/bash\nset -e\ncd /app && npx tsc --outDir /tmp/dist 2>&1 >&2\nln -s /app/node_modules /tmp/dist/node_modules\nchmod -R a-w /tmp/dist\ncat > /tmp/input.json\nnode /tmp/dist/index.js < /tmp/input.json\n' > /app/entrypoint.sh && chmod +x /app/entrypoint.sh
# Set ownership to node user (non-root) for writable directories # Set ownership to node user (non-root) for writable directories
RUN chown -R node:node /workspace RUN chown -R node:node /workspace && chmod 777 /home/node
# Switch to non-root user (required for --dangerously-skip-permissions) # Switch to non-root user (required for --dangerously-skip-permissions)
USER node USER node

View File

@@ -192,6 +192,16 @@ function readSecrets(): Record<string, string> {
function buildContainerArgs(mounts: VolumeMount[], containerName: string): string[] { function buildContainerArgs(mounts: VolumeMount[], containerName: string): string[] {
const args: string[] = ['run', '-i', '--rm', '--name', containerName]; const args: string[] = ['run', '-i', '--rm', '--name', containerName];
// Run as host user so bind-mounted files are accessible.
// Skip when running as root (uid 0), as the container's node user (uid 1000),
// or when getuid is unavailable (native Windows without WSL).
const hostUid = process.getuid?.();
const hostGid = process.getgid?.();
if (hostUid != null && hostUid !== 0 && hostUid !== 1000) {
args.push('--user', `${hostUid}:${hostGid}`);
args.push('-e', 'HOME=/home/node');
}
// Apple Container: --mount for readonly, -v for read-write // Apple Container: --mount for readonly, -v for read-write
for (const mount of mounts) { for (const mount of mounts) {
if (mount.readonly) { if (mount.readonly) {

View File

@@ -41,10 +41,10 @@ function askQuestion(prompt: string): Promise<string> {
}); });
} }
async function connectSocket(phoneNumber?: string): Promise<void> { async function connectSocket(phoneNumber?: string, isReconnect = false): Promise<void> {
const { state, saveCreds } = await useMultiFileAuthState(AUTH_DIR); const { state, saveCreds } = await useMultiFileAuthState(AUTH_DIR);
if (state.creds.registered) { if (state.creds.registered && !isReconnect) {
fs.writeFileSync(STATUS_FILE, 'already_authenticated'); fs.writeFileSync(STATUS_FILE, 'already_authenticated');
console.log('✓ Already authenticated with WhatsApp'); console.log('✓ Already authenticated with WhatsApp');
console.log( console.log(
@@ -110,7 +110,7 @@ async function connectSocket(phoneNumber?: string): Promise<void> {
// 515 = stream error, often happens after pairing succeeds but before // 515 = stream error, often happens after pairing succeeds but before
// registration completes. Reconnect to finish the handshake. // registration completes. Reconnect to finish the handshake.
console.log('\n⟳ Stream error (515) after pairing — reconnecting...'); console.log('\n⟳ Stream error (515) after pairing — reconnecting...');
connectSocket(phoneNumber); connectSocket(phoneNumber, true);
} else { } else {
fs.writeFileSync(STATUS_FILE, `failed:${reason || 'unknown'}`); fs.writeFileSync(STATUS_FILE, `failed:${reason || 'unknown'}`);
console.log('\n✗ Connection failed. Please try again.'); console.log('\n✗ Connection failed. Please try again.');