remcode
Version:
Turn your AI assistant into a codebase expert. Intelligent code analysis, semantic search, and software engineering guidance through MCP integration.
274 lines (273 loc) • 10.6 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.SecretsManager = void 0;
const sodium = __importStar(require("libsodium-wrappers"));
// Use proper import syntax for Octokit with types
const rest_1 = require("@octokit/rest");
const logger_1 = require("../utils/logger");
const logger = (0, logger_1.getLogger)('SecretsManager');
/**
* Class to manage GitHub repository secrets for Remcode
*/
class SecretsManager {
/**
* Constructor
* @param githubToken GitHub API token
*/
constructor(githubToken) {
this.octokit = null;
this.token = null;
this.token = githubToken || process.env.GITHUB_TOKEN || null;
if (this.token) {
this.octokit = new rest_1.Octokit({ auth: this.token });
logger.debug('GitHub API client initialized');
}
else {
logger.warn('No GitHub token provided, API operations will be simulated');
}
}
/**
* Configure required repository secrets
* @param owner Repository owner
* @param repo Repository name
* @returns Summary of secret operations
*/
async configureRepositorySecrets(owner, repo) {
logger.info(`Configuring repository secrets for ${owner}/${repo}`);
const secrets = this.getRequiredSecrets();
const results = [];
for (const secret of secrets) {
try {
const result = await this.setRepositorySecret(owner, repo, secret);
results.push(result);
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.error(`Failed to set secret ${secret.name}: ${errorMessage}`);
results.push({
success: false,
secretName: secret.name,
error: errorMessage
});
// If this is a required secret, we should abort
if (secret.required) {
throw new Error(`Failed to set required secret ${secret.name}: ${errorMessage}`);
}
}
}
// Calculate summary
const successful = results.filter(r => r.success).length;
const summary = {
total: secrets.length,
successful,
failed: secrets.length - successful,
results
};
logger.info(`Secret configuration complete: ${successful}/${secrets.length} secrets configured`);
return summary;
}
/**
* Get required secrets for Remcode
* @returns List of required secrets
*/
getRequiredSecrets() {
return [
{
name: 'PINECONE_API_KEY',
value: process.env.PINECONE_API_KEY || '',
description: 'Pinecone API key for vector storage',
required: true
},
{
name: 'HUGGINGFACE_TOKEN',
value: process.env.HUGGINGFACE_TOKEN || '',
description: 'HuggingFace token for embeddings',
required: true
},
{
name: 'OPENAI_API_KEY',
value: process.env.OPENAI_API_KEY || '',
description: 'OpenAI API key for advanced text processing',
required: false
},
{
name: 'SLACK_WEBHOOK_URL',
value: process.env.SLACK_WEBHOOK_URL || '',
description: 'Slack webhook URL for notifications',
required: false
}
];
}
/**
* Set a repository secret in GitHub
* @param owner Repository owner
* @param repo Repository name
* @param secret Secret configuration
* @returns Secret operation result
*/
async setRepositorySecret(owner, repo, secret) {
logger.info(`Setting repository secret: ${secret.name}`);
// Validate secret value
if (!secret.value) {
if (secret.required) {
throw new Error(`Missing value for required secret: ${secret.name}`);
}
else {
logger.warn(`Missing value for optional secret: ${secret.name}, skipping`);
return {
success: false,
secretName: secret.name,
error: 'Missing value for optional secret'
};
}
}
try {
if (!this.octokit) {
// Simulate API call if no GitHub token is available
logger.info(`[SIMULATED] Secret ${secret.name} would be set for ${owner}/${repo}`);
return {
success: true,
secretName: secret.name
};
}
// Get the repository's public key for secret encryption
const { data: publicKeyData } = await this.octokit.actions.getRepoPublicKey({
owner,
repo
});
// Encrypt the secret value with the repository's public key
const encryptedValue = await this.encryptSecret(publicKeyData.key, secret.value);
// Create or update the secret
await this.octokit.actions.createOrUpdateRepoSecret({
owner,
repo,
secret_name: secret.name,
encrypted_value: encryptedValue,
key_id: publicKeyData.key_id
});
logger.info(`Secret ${secret.name} successfully set for ${owner}/${repo}`);
return {
success: true,
secretName: secret.name
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.error(`Failed to set secret ${secret.name}: ${errorMessage}`);
return {
success: false,
secretName: secret.name,
error: errorMessage
};
}
}
/**
* Encrypt a secret value using sodium for GitHub
* @param publicKey Base64-encoded public key
* @param secretValue Secret value to encrypt
* @returns Base64-encoded encrypted secret
*/
async encryptSecret(publicKey, secretValue) {
// Wait for sodium to initialize
await sodium.ready;
// Convert the public key to a Uint8Array
const publicKeyBytes = sodium.from_base64(publicKey, sodium.base64_variants.ORIGINAL);
// Convert the secret to a Uint8Array
const secretBytes = sodium.from_string(secretValue);
// Encrypt the secret using libsodium
const encryptedBytes = sodium.crypto_box_seal(secretBytes, publicKeyBytes);
// Convert the encrypted secret to Base64
return sodium.to_base64(encryptedBytes, sodium.base64_variants.ORIGINAL);
}
/**
* Check if a repository has a specific secret
* @param owner Repository owner
* @param repo Repository name
* @param secretName Secret name to check
* @returns True if the secret exists
*/
async hasRepositorySecret(owner, repo, secretName) {
try {
if (!this.octokit) {
// Simulate API call if no GitHub token is available
logger.info(`[SIMULATED] Checking if secret ${secretName} exists for ${owner}/${repo}`);
return false;
}
// Get the list of repository secrets
const { data } = await this.octokit.actions.listRepoSecrets({
owner,
repo
});
// Check if the secret exists
return data.secrets.some((s) => s.name === secretName);
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.error(`Failed to check if secret ${secretName} exists: ${errorMessage}`);
return false;
}
}
/**
* Delete a repository secret
* @param owner Repository owner
* @param repo Repository name
* @param secretName Secret name to delete
* @returns True if the secret was deleted
*/
async deleteRepositorySecret(owner, repo, secretName) {
try {
if (!this.octokit) {
// Simulate API call if no GitHub token is available
logger.info(`[SIMULATED] Deleting secret ${secretName} from ${owner}/${repo}`);
return true;
}
// Delete the secret
await this.octokit.actions.deleteRepoSecret({
owner,
repo,
secret_name: secretName
});
logger.info(`Secret ${secretName} successfully deleted from ${owner}/${repo}`);
return true;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
logger.error(`Failed to delete secret ${secretName}: ${errorMessage}`);
return false;
}
}
}
exports.SecretsManager = SecretsManager;
;