diff --git a/COMMAND_STREAM_ISSUE.md b/COMMAND_STREAM_ISSUE.md new file mode 100644 index 0000000..a574fea --- /dev/null +++ b/COMMAND_STREAM_ISSUE.md @@ -0,0 +1,63 @@ +# Command-Stream JSON Quoting Issue Tracking + +## Issue Summary +The `claude-profiles.mjs` script cannot use command-stream for macOS keychain storage commands due to automatic quote wrapping that corrupts JSON data. + +## Affected Code Location +- **File**: `claude-profiles.mjs` +- **Line**: 387-391 (commented out code) +- **Function**: `setKeychainCredentials()` + +## Problem Description +Command-stream's `$` template function automatically wraps interpolated values in single quotes, which corrupts JSON strings: + +```javascript +// โŒ Current issue (line 387-391 in claude-profiles.mjs) +const result = await $`security add-generic-password -U -a $USER -s "Claude Code-credentials" -w "${escapedJson}"`; +// Results in: '{"claudeAiOauth":{...}}' โ† Invalid JSON with wrapping quotes +// Expected: {"claudeAiOauth":{...}} โ† Valid JSON +``` + +## Current Workaround +Using Node.js `execSync` instead (lines 372-376): +```javascript +const command = `security add-generic-password -U -a $USER -s "Claude Code-credentials" -w "${escapedJson}"`; +execSync(command, { shell: true, stdio: isVerbose ? 'inherit' : 'ignore' }); +``` + +## Upstream Issue Tracking +- **Repository**: https://github.com/link-foundation/command-stream +- **Issue #39**: JSON strings with quotes cause escaping issues +- **Issue #45**: Automatic quote addition in interpolation causes issues +- **Status**: OPEN (as of 2025-09-10) +- **Our Comment**: https://github.com/link-foundation/command-stream/issues/39#issuecomment-3275976359 + +## Monitoring for Fix +To check if the issue has been resolved: + +1. **Check issue status**: Visit issue #39 and #45 on the command-stream repository +2. **Test with reproduction script**: Run `examples/command-stream-json-issue-reproduction.mjs` +3. **Check for new releases**: Monitor https://www.npmjs.com/package/command-stream for versions > 0.7.0 + +## How to Update When Fixed +When the issue is resolved: + +1. Uncomment the command-stream code (lines 387-391) +2. Remove or comment the execSync workaround (lines 372-376) +3. Test with various JSON payloads to ensure reliability +4. Update the version constraint in the use() call if needed + +## Testing the Fix +Use the reproduction script to verify the fix: +```bash +node examples/command-stream-json-issue-reproduction.mjs +``` + +Expected output after fix: +- JSON should not be wrapped in extra single quotes +- Command output should show valid JSON format +- No escaping issues with nested quotes + +## Related Files +- `claude-profiles.mjs` - Main script with the affected code +- `examples/command-stream-json-issue-reproduction.mjs` - Test reproduction script \ No newline at end of file diff --git a/claude-profiles.mjs b/claude-profiles.mjs index de3d92a..b1bd31b 100755 --- a/claude-profiles.mjs +++ b/claude-profiles.mjs @@ -384,6 +384,10 @@ async function setKeychainCredentials(credentials) { * * This makes the JSON invalid and Claude Code cannot parse it. * + * ISSUE REPORTED: https://github.com/link-foundation/command-stream/issues/39 + * MONITORING: Run `node examples/check-command-stream-fix.mjs` to check for fix + * DOCUMENTATION: See COMMAND_STREAM_ISSUE.md for full details + * * const result = await $`security add-generic-password -U -a $USER -s "Claude Code-credentials" -w "${escapedJson}"`.run({ * capture: true, * mirror: false diff --git a/examples/check-command-stream-fix.mjs b/examples/check-command-stream-fix.mjs new file mode 100644 index 0000000..446868b --- /dev/null +++ b/examples/check-command-stream-fix.mjs @@ -0,0 +1,82 @@ +#!/usr/bin/env node +// Script to check if command-stream JSON quoting issue has been fixed +// Run this periodically to monitor for fix availability + +import { execSync } from 'child_process'; + +console.log('๐Ÿ” Checking command-stream JSON quoting issue status...\n'); + +// Check if we can load command-stream +let commandStreamVersion; +try { + const { use } = eval( + await fetch('https://unpkg.com/use-m/use.js').then(r => r.text()) + ); + const [{ $ }] = await Promise.all([ + use('command-stream@latest') // Use latest to check for fixes + ]); + + // Try to get version info + try { + const versionInfo = execSync('npm view command-stream version', { + encoding: 'utf8', + stdio: 'pipe' + }).trim(); + commandStreamVersion = versionInfo; + console.log(`๐Ÿ“ฆ Command-stream version: ${commandStreamVersion}`); + } catch (e) { + console.log('๐Ÿ“ฆ Command-stream loaded (version unknown)'); + } + + // Test the JSON quoting issue + const testJson = '{"test": "value with \\"quotes\\""}'; + const result = await $`echo "JSON: ${testJson}"`.run({ + capture: true, + mirror: false + }); + + const output = result.stdout.trim(); + const extractedJson = output.replace('JSON: ', ''); + + console.log('\n๐Ÿงช Test Results:'); + console.log('Input JSON :', testJson); + console.log('Output :', extractedJson); + + // Check if JSON is properly handled (not wrapped in extra quotes) + let isFixed = false; + try { + // If it's properly handled, we should be able to parse it + const parsed = JSON.parse(extractedJson); + if (parsed && typeof parsed === 'object') { + isFixed = true; + console.log('โœ… FIXED: JSON is properly handled without extra quoting!'); + console.log('๐ŸŽ‰ You can now update claude-profiles.mjs to use command-stream'); + } + } catch (parseError) { + console.log('โŒ NOT FIXED: JSON is still corrupted by extra quoting'); + console.log(' Expected:', testJson); + console.log(' Got :', extractedJson); + } + + // Additional checks + console.log('\n๐Ÿ“Š Status:'); + if (isFixed) { + console.log('๐ŸŸข Issue Status: RESOLVED'); + console.log('๐Ÿ”ง Action Needed: Update claude-profiles.mjs to use command-stream'); + console.log('๐Ÿ“ Update Instructions: See COMMAND_STREAM_ISSUE.md'); + } else { + console.log('๐Ÿ”ด Issue Status: NOT YET RESOLVED'); + console.log('โณ Action Needed: Continue monitoring for fix'); + console.log('๐Ÿ”— Track: https://github.com/link-foundation/command-stream/issues/39'); + } + +} catch (error) { + console.error('โŒ Error testing command-stream:', error.message); + console.log('\n๐Ÿ’ก This might indicate:'); + console.log(' - Network connectivity issues'); + console.log(' - Command-stream package issues'); + console.log(' - Breaking changes in the library'); +} + +console.log('\n๐Ÿ“… Last checked:', new Date().toISOString()); +console.log('๐Ÿ”„ Run this script periodically to monitor for fixes'); \ No newline at end of file diff --git a/examples/command-stream-json-issue-reproduction.mjs b/examples/command-stream-json-issue-reproduction.mjs new file mode 100644 index 0000000..1fb80b5 --- /dev/null +++ b/examples/command-stream-json-issue-reproduction.mjs @@ -0,0 +1,57 @@ +#!/usr/bin/env node +// Minimal reproduction of command-stream JSON quoting issue affecting claude-profiles.mjs + +// This is the same import used in claude-profiles.mjs +const { use } = eval( + await fetch('https://unpkg.com/use-m/use.js').then(r => r.text()) +); +const [{ $ }] = await Promise.all([ + use('command-stream@0.7.0') +]); + +// Sample credentials data similar to what claude-profiles.mjs handles +const credentials = { + claudeAiOauth: { + accessToken: "sample_access_token_123", + refreshToken: "sample_refresh_token_456", + name: "Test User", + description: "User with \"quotes\" and 'apostrophes'" + } +}; + +console.log('Testing command-stream JSON interpolation issue...\n'); + +const jsonStr = JSON.stringify(credentials); +const escapedJson = jsonStr.replace(/"/g, '\\"'); // Escape quotes like in claude-profiles.mjs + +console.log('Original JSON:', jsonStr); +console.log('Escaped JSON:', escapedJson); + +try { + // This is the exact command that fails in claude-profiles.mjs line 387 + console.log('\nTesting command-stream $ interpolation:'); + const result = await $`echo "JSON would be: ${escapedJson}"`.run({ + capture: true, + mirror: false + }); + + console.log('Command output:', result.stdout); + console.log('Expected: JSON with proper double quotes'); + console.log('Actual: JSON wrapped in single quotes (invalid for parsing)'); + + // Show the difference + console.log('\nActual behavior:'); + console.log('command-stream wraps the entire JSON in single quotes:'); + console.log("'", result.stdout.trim().replace('JSON would be: ', ''), "'"); + + console.log('\nExpected behavior:'); + console.log('JSON should remain as valid JSON without extra quoting:'); + console.log(jsonStr); + +} catch (error) { + console.error('Command failed:', error.message); +} + +console.log('\nThis issue prevents claude-profiles.mjs from using command-stream'); +console.log('for the keychain storage command at line 387.'); +console.log('The workaround is using Node.js execSync instead.'); \ No newline at end of file