mirror of
https://github.com/blackboxprogramming/context-bridge.git
synced 2026-03-17 03:57:14 -05:00
Agent Coordination: - Epimetheus (Architect) identity assigned and registered - Connected to PS-SHA-∞ memory system (4,059 entries) - Task claimed from marketplace - Broadcasting to other agents Launch Documentation Created: - PUBLISH_TO_NPM.md - Complete npm publishing guide - STRIPE_LIVE_SETUP.md - Stripe live mode setup guide - AGENT_COORDINATION_REPORT.md - Full status and next steps - EPIMETHEUS_SESSION_COMPLETE.md - Session summary - Added all previous documentation to repo Launch Status: 98% Complete Blocked on: User actions (npm login + Stripe products) Ready: Screenshots, testing, submissions, announcements Next Steps: 1. User: npm login && npm publish (10 min) 2. User: Create Stripe products (5 min) 3. Capture 5 screenshots (15 min) 4. Manual testing on 4 platforms (20 min) 5. Submit to Chrome Web Store (30 min) 6. Launch announcements (10 min) Total time to launch: ~90 minutes Agent Body: qwen2.5-coder:7b (open source) Memory Hash: 4e3d2012 Collaboration: ACTIVE Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
204 lines
5.5 KiB
JavaScript
204 lines
5.5 KiB
JavaScript
const { Octokit } = require('@octokit/rest');
|
|
const { getConfig } = require('./config');
|
|
|
|
// Helper function to handle API errors with retry
|
|
async function withRetry(fn, retries = 3, delay = 1000) {
|
|
for (let i = 0; i < retries; i++) {
|
|
try {
|
|
return await fn();
|
|
} catch (error) {
|
|
if (i === retries - 1) throw error;
|
|
|
|
// Check if error is retryable
|
|
const isRetryable =
|
|
error.status === 502 || // Bad Gateway
|
|
error.status === 503 || // Service Unavailable
|
|
error.status === 504 || // Gateway Timeout
|
|
error.code === 'ECONNRESET' ||
|
|
error.code === 'ETIMEDOUT';
|
|
|
|
if (!isRetryable) throw error;
|
|
|
|
// Exponential backoff
|
|
await new Promise(resolve => setTimeout(resolve, delay * Math.pow(2, i)));
|
|
}
|
|
}
|
|
}
|
|
|
|
// Helper to create better error messages
|
|
function enhanceError(error, context) {
|
|
const messages = {
|
|
401: 'Authentication failed. Your GitHub token may be invalid or expired.\nRun: context login',
|
|
403: error.message.includes('rate limit')
|
|
? 'GitHub API rate limit exceeded. Try again in an hour, or use a different GitHub account.'
|
|
: 'Access forbidden. Check that your token has the "gist" scope.',
|
|
404: 'Gist not found. It may have been deleted.\nRun: context init',
|
|
422: 'Invalid request. The gist content may be too large (max 10MB).',
|
|
500: 'GitHub server error. Try again in a few moments.',
|
|
503: 'GitHub is temporarily unavailable. Try again in a few moments.'
|
|
};
|
|
|
|
const statusCode = error.status;
|
|
const message = messages[statusCode] || error.message;
|
|
|
|
const enhanced = new Error(`${context}: ${message}`);
|
|
enhanced.originalError = error;
|
|
enhanced.status = statusCode;
|
|
return enhanced;
|
|
}
|
|
|
|
async function createGist(content, description = 'My AI Context - Context Bridge') {
|
|
const config = getConfig();
|
|
if (!config.token) {
|
|
throw new Error('Not authenticated. Run: context login');
|
|
}
|
|
|
|
// Validate content size (10MB limit)
|
|
const sizeInMB = Buffer.byteLength(content, 'utf8') / (1024 * 1024);
|
|
if (sizeInMB > 10) {
|
|
throw new Error(`Context is too large (${sizeInMB.toFixed(2)}MB). GitHub gists have a 10MB limit.`);
|
|
}
|
|
|
|
const octokit = new Octokit({ auth: config.token });
|
|
|
|
try {
|
|
const result = await withRetry(async () => {
|
|
return await octokit.gists.create({
|
|
description,
|
|
public: false,
|
|
files: {
|
|
'CONTEXT.md': {
|
|
content
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
const { data } = result;
|
|
|
|
return {
|
|
id: data.id,
|
|
url: data.html_url,
|
|
rawUrl: data.files['CONTEXT.md'].raw_url
|
|
};
|
|
} catch (error) {
|
|
throw enhanceError(error, 'Failed to create gist');
|
|
}
|
|
}
|
|
|
|
async function updateGist(content, message = null) {
|
|
const config = getConfig();
|
|
if (!config.token) {
|
|
throw new Error('Not authenticated. Run: context login');
|
|
}
|
|
if (!config.gistId) {
|
|
throw new Error('No context initialized. Run: context init');
|
|
}
|
|
|
|
// Validate content size
|
|
const sizeInMB = Buffer.byteLength(content, 'utf8') / (1024 * 1024);
|
|
if (sizeInMB > 10) {
|
|
throw new Error(`Context is too large (${sizeInMB.toFixed(2)}MB). GitHub gists have a 10MB limit.`);
|
|
}
|
|
|
|
const octokit = new Octokit({ auth: config.token });
|
|
|
|
const description = message
|
|
? `My AI Context - ${message} (${new Date().toISOString().split('T')[0]})`
|
|
: `My AI Context - Updated ${new Date().toISOString().split('T')[0]}`;
|
|
|
|
try {
|
|
const result = await withRetry(async () => {
|
|
return await octokit.gists.update({
|
|
gist_id: config.gistId,
|
|
description,
|
|
files: {
|
|
'CONTEXT.md': {
|
|
content
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
const { data } = result;
|
|
|
|
return {
|
|
id: data.id,
|
|
url: data.html_url,
|
|
rawUrl: data.files['CONTEXT.md'].raw_url
|
|
};
|
|
} catch (error) {
|
|
throw enhanceError(error, 'Failed to update gist');
|
|
}
|
|
}
|
|
|
|
async function getGist() {
|
|
const config = getConfig();
|
|
if (!config.token) {
|
|
throw new Error('Not authenticated. Run: context login');
|
|
}
|
|
if (!config.gistId) {
|
|
throw new Error('No context initialized. Run: context init');
|
|
}
|
|
|
|
const octokit = new Octokit({ auth: config.token });
|
|
|
|
try {
|
|
const result = await withRetry(async () => {
|
|
return await octokit.gists.get({
|
|
gist_id: config.gistId
|
|
});
|
|
});
|
|
|
|
const { data } = result;
|
|
|
|
return {
|
|
content: data.files['CONTEXT.md'].content,
|
|
url: data.html_url,
|
|
rawUrl: data.files['CONTEXT.md'].raw_url,
|
|
updatedAt: data.updated_at,
|
|
revisions: data.history.length
|
|
};
|
|
} catch (error) {
|
|
throw enhanceError(error, 'Failed to fetch gist');
|
|
}
|
|
}
|
|
|
|
async function getGistHistory(limit = 10) {
|
|
const config = getConfig();
|
|
if (!config.token) {
|
|
throw new Error('Not authenticated. Run: context login');
|
|
}
|
|
if (!config.gistId) {
|
|
throw new Error('No context initialized. Run: context init');
|
|
}
|
|
|
|
const octokit = new Octokit({ auth: config.token });
|
|
|
|
try {
|
|
const result = await withRetry(async () => {
|
|
return await octokit.gists.get({
|
|
gist_id: config.gistId
|
|
});
|
|
});
|
|
|
|
const { data } = result;
|
|
|
|
return data.history.slice(0, limit).map(h => ({
|
|
version: h.version,
|
|
committedAt: h.committed_at,
|
|
changeStatus: h.change_status,
|
|
user: h.user.login
|
|
}));
|
|
} catch (error) {
|
|
throw enhanceError(error, 'Failed to fetch gist history');
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
createGist,
|
|
updateGist,
|
|
getGist,
|
|
getGistHistory
|
|
};
|