d2e926fbf7
* fix: add gemini-3-flash to validModels array The model was defined in the type union and RPM limits but missing from the runtime validModels array, causing silent fallback to gemini-2.5-flash. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: skip processing when Gemini returns empty observation response Empty responses were silently consuming messages from the queue via processAgentResponse. Now skips processing on empty content, leaving the message in processing status for stale recovery. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: prevent idle timeout from triggering infinite restart loop When a session hits the 3-minute idle timeout, the finally block was seeing stale processing messages and restarting the generator endlessly. Now tracks idle timeout as a distinct exit reason via session flag, resets stale messages, and skips restart. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: clear stale Bun native module cache on update Bun's global cache retains sharp/libvips native binaries with broken dylib references after version upgrades. Clear ~/.bun/install/cache/@img/ before install in both the end-user (smart-install) and dev (sync-marketplace) paths to prevent ERR_DLOPEN_FAILED errors in Chroma sync. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR review feedback (empty summary response, session-scoped reset, shell injection) - Apply same empty-response guard to summary path as observation path in GeminiAgent - Add optional sessionDbId param to resetStaleProcessingMessages for session-scoped resets - Use JSON.stringify for gitignore pattern escaping, filter negation patterns Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
150 lines
5.0 KiB
JavaScript
150 lines
5.0 KiB
JavaScript
#!/usr/bin/env node
|
||
/**
|
||
* Protected sync-marketplace script
|
||
*
|
||
* Prevents accidental rsync overwrite when installed plugin is on beta branch.
|
||
* If on beta, the user should use the UI to update instead.
|
||
*/
|
||
|
||
const { execSync } = require('child_process');
|
||
const { existsSync, readFileSync } = require('fs');
|
||
const path = require('path');
|
||
const os = require('os');
|
||
|
||
const INSTALLED_PATH = path.join(os.homedir(), '.claude', 'plugins', 'marketplaces', 'thedotmack');
|
||
const CACHE_BASE_PATH = path.join(os.homedir(), '.claude', 'plugins', 'cache', 'thedotmack', 'claude-mem');
|
||
|
||
function getCurrentBranch() {
|
||
try {
|
||
if (!existsSync(path.join(INSTALLED_PATH, '.git'))) {
|
||
return null;
|
||
}
|
||
return execSync('git rev-parse --abbrev-ref HEAD', {
|
||
cwd: INSTALLED_PATH,
|
||
encoding: 'utf-8',
|
||
stdio: ['pipe', 'pipe', 'pipe']
|
||
}).trim();
|
||
} catch {
|
||
return null;
|
||
}
|
||
}
|
||
|
||
function getGitignoreExcludes(basePath) {
|
||
const gitignorePath = path.join(basePath, '.gitignore');
|
||
if (!existsSync(gitignorePath)) return '';
|
||
|
||
const lines = readFileSync(gitignorePath, 'utf-8').split('\n');
|
||
return lines
|
||
.map(line => line.trim())
|
||
.filter(line => line && !line.startsWith('#') && !line.startsWith('!'))
|
||
.map(pattern => `--exclude=${JSON.stringify(pattern)}`)
|
||
.join(' ');
|
||
}
|
||
|
||
const branch = getCurrentBranch();
|
||
const isForce = process.argv.includes('--force');
|
||
|
||
if (branch && branch !== 'main' && !isForce) {
|
||
console.log('');
|
||
console.log('\x1b[33m%s\x1b[0m', `WARNING: Installed plugin is on beta branch: ${branch}`);
|
||
console.log('\x1b[33m%s\x1b[0m', 'Running rsync would overwrite beta code.');
|
||
console.log('');
|
||
console.log('Options:');
|
||
console.log(' 1. Use UI at http://localhost:37777 to update beta');
|
||
console.log(' 2. Switch to stable in UI first, then run sync');
|
||
console.log(' 3. Force rsync: npm run sync-marketplace:force');
|
||
console.log('');
|
||
process.exit(1);
|
||
}
|
||
|
||
// Get version from plugin.json
|
||
function getPluginVersion() {
|
||
try {
|
||
const pluginJsonPath = path.join(__dirname, '..', 'plugin', '.claude-plugin', 'plugin.json');
|
||
const pluginJson = JSON.parse(readFileSync(pluginJsonPath, 'utf-8'));
|
||
return pluginJson.version;
|
||
} catch (error) {
|
||
console.error('\x1b[31m%s\x1b[0m', 'Failed to read plugin version:', error.message);
|
||
process.exit(1);
|
||
}
|
||
}
|
||
|
||
// Normal rsync for main branch or fresh install
|
||
console.log('Syncing to marketplace...');
|
||
try {
|
||
const rootDir = path.join(__dirname, '..');
|
||
const gitignoreExcludes = getGitignoreExcludes(rootDir);
|
||
|
||
execSync(
|
||
`rsync -av --delete --exclude=.git --exclude=/.mcp.json --exclude=bun.lock --exclude=package-lock.json ${gitignoreExcludes} ./ ~/.claude/plugins/marketplaces/thedotmack/`,
|
||
{ stdio: 'inherit' }
|
||
);
|
||
|
||
// Remove stale lockfiles before install — they pin old native dep versions
|
||
const { unlinkSync, rmSync } = require('fs');
|
||
for (const lockfile of ['package-lock.json', 'bun.lock']) {
|
||
const lockpath = path.join(INSTALLED_PATH, lockfile);
|
||
if (existsSync(lockpath)) {
|
||
unlinkSync(lockpath);
|
||
console.log(`Removed stale ${lockfile}`);
|
||
}
|
||
}
|
||
|
||
// Clear stale native module cache (sharp/libvips) — Bun's cache can retain
|
||
// native binaries that reference companion libraries at broken relative paths
|
||
const bunCacheImgDir = path.join(os.homedir(), '.bun', 'install', 'cache', '@img');
|
||
if (existsSync(bunCacheImgDir)) {
|
||
rmSync(bunCacheImgDir, { recursive: true, force: true });
|
||
console.log('Cleared stale native module cache (@img/sharp)');
|
||
}
|
||
|
||
console.log('Running npm install in marketplace...');
|
||
execSync(
|
||
'cd ~/.claude/plugins/marketplaces/thedotmack/ && npm install',
|
||
{ stdio: 'inherit' }
|
||
);
|
||
|
||
// Sync to cache folder with version
|
||
const version = getPluginVersion();
|
||
const CACHE_VERSION_PATH = path.join(CACHE_BASE_PATH, version);
|
||
|
||
const pluginDir = path.join(rootDir, 'plugin');
|
||
const pluginGitignoreExcludes = getGitignoreExcludes(pluginDir);
|
||
|
||
console.log(`Syncing to cache folder (version ${version})...`);
|
||
execSync(
|
||
`rsync -av --delete --exclude=.git ${pluginGitignoreExcludes} plugin/ "${CACHE_VERSION_PATH}/"`,
|
||
{ stdio: 'inherit' }
|
||
);
|
||
|
||
console.log('\x1b[32m%s\x1b[0m', 'Sync complete!');
|
||
|
||
// Trigger worker restart after file sync
|
||
console.log('\n🔄 Triggering worker restart...');
|
||
const http = require('http');
|
||
const req = http.request({
|
||
hostname: '127.0.0.1',
|
||
port: 37777,
|
||
path: '/api/admin/restart',
|
||
method: 'POST',
|
||
timeout: 2000
|
||
}, (res) => {
|
||
if (res.statusCode === 200) {
|
||
console.log('\x1b[32m%s\x1b[0m', '✓ Worker restart triggered');
|
||
} else {
|
||
console.log('\x1b[33m%s\x1b[0m', `ℹ Worker restart returned status ${res.statusCode}`);
|
||
}
|
||
});
|
||
req.on('error', () => {
|
||
console.log('\x1b[33m%s\x1b[0m', 'ℹ Worker not running, will start on next hook');
|
||
});
|
||
req.on('timeout', () => {
|
||
req.destroy();
|
||
console.log('\x1b[33m%s\x1b[0m', 'ℹ Worker restart timed out');
|
||
});
|
||
req.end();
|
||
|
||
} catch (error) {
|
||
console.error('\x1b[31m%s\x1b[0m', 'Sync failed:', error.message);
|
||
process.exit(1);
|
||
} |