codexmobile-relay / scripts /prepare-hf-space.mjs
Codex
deploy: CodexMobile Relay
90f0300
Raw
History Blame Contribute Delete
6.14 kB
import fs from 'node:fs';
import path from 'node:path';
const ROOT_DIR = path.resolve(import.meta.dirname, '..');
const DEFAULT_OUTPUT_DIR = path.join(ROOT_DIR, 'dist', 'hf-space');
const COPY_PATHS = [
'Dockerfile',
'package.json',
'package-lock.json',
'client',
'server',
'scripts'
];
const EXCLUDED_NAMES = new Set([
'.DS_Store',
'.env',
'.codexmobile',
'.git',
'node_modules',
'dist',
'coverage',
'.vite',
'.cache'
]);
const EXCLUDED_SCRIPT_NAMES = new Set([
'start-all.ps1',
'start-server.mjs',
'start-asr.mjs'
]);
const SPACE_README = `---
title: CodexMobile Relay
sdk: docker
app_port: 7860
---
# CodexMobile Relay
This HuggingFace Docker Space runs the CodexMobile relay entrypoint.
Configure Space variables:
\`\`\`text
CODEXMOBILE_MODE=relay
HOST=0.0.0.0
PORT=7860
CODEXMOBILE_RELAY_REQUEST_TIMEOUT_MS=120000
CODEXMOBILE_RELAY_HEARTBEAT_MS=15000
CODEXMOBILE_RELAY_IDLE_HEARTBEAT_MS=300000
CODEXMOBILE_RELAY_PENDING_REQUESTS_MAX=64
CODEXMOBILE_RELAY_BROWSER_PENDING_REQUESTS_MAX=6
CODEXMOBILE_RELAY_TOKEN_REQUESTS_PER_MINUTE=120
CODEXMOBILE_RELAY_TOKEN_REQUEST_WINDOW_MS=60000
\`\`\`
Mac connector defaults to an unauthenticated HTTP keepalive against
\`/api/status?keepalive=1\` every 240000 ms. Active intervals below 60000 ms are
ignored. Set \`CODEXMOBILE_RELAY_KEEPALIVE_MS=0\` on the connector only when you
explicitly want to disable that Space wakeup traffic.
Configure Space secrets:
\`\`\`text
CODEXMOBILE_RELAY_SECRET=<at-least-32-character-random-secret>
CODEXMOBILE_RELAY_PREVIOUS_SECRET=<optional-previous-secret-during-rotation>
\`\`\`
Do not commit relay secrets, browser tokens, pairing codes, logs, or local state.
`;
function parseArgs(argv) {
const options = {
outDir: DEFAULT_OUTPUT_DIR
};
for (let index = 0; index < argv.length; index += 1) {
const item = argv[index];
if (item === '--out') {
const value = argv[index + 1];
if (!value) {
throw new Error('--out requires a directory.');
}
options.outDir = path.resolve(ROOT_DIR, value);
index += 1;
continue;
}
if (item.startsWith('--out=')) {
options.outDir = path.resolve(ROOT_DIR, item.slice('--out='.length));
continue;
}
throw new Error(`Unknown argument: ${item}`);
}
return options;
}
function assertSafeOutputDir(outDir) {
const resolved = path.resolve(outDir);
const distDir = path.join(ROOT_DIR, 'dist');
if (resolved === ROOT_DIR || resolved === distDir) {
throw new Error('Refusing to overwrite the repository root or dist root.');
}
const relativeToDist = path.relative(distDir, resolved);
if (isOutsideRelativePath(relativeToDist)) {
throw new Error('Output directory must be inside dist/.');
}
assertExistingAncestorInsideDist(nearestExistingPath(resolved), distDir);
}
function isOutsideRelativePath(relativePath) {
return relativePath === '..' || relativePath.startsWith(`..${path.sep}`) || path.isAbsolute(relativePath);
}
function nearestExistingPath(targetPath) {
let current = targetPath;
while (true) {
try {
fs.lstatSync(current);
return current;
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
const parent = path.dirname(current);
if (parent === current) {
throw error;
}
current = parent;
}
}
}
function assertExistingAncestorInsideDist(existingPath, distDir) {
const resolvedRoot = fs.realpathSync(ROOT_DIR);
const resolvedExisting = fs.realpathSync(existingPath);
const relativeToRoot = path.relative(resolvedRoot, resolvedExisting);
if (isOutsideRelativePath(relativeToRoot)) {
throw new Error('Output directory must be inside dist/.');
}
if (!fs.existsSync(distDir)) {
return;
}
const resolvedDist = fs.realpathSync(distDir);
const relativeToDist = path.relative(resolvedDist, resolvedExisting);
if (isOutsideRelativePath(relativeToDist)) {
throw new Error('Output directory must be inside dist/.');
}
}
function shouldCopy(sourcePath) {
const name = path.basename(sourcePath);
if (EXCLUDED_NAMES.has(name)) {
return false;
}
if (path.basename(path.dirname(sourcePath)) === 'scripts' && EXCLUDED_SCRIPT_NAMES.has(name)) {
return false;
}
return true;
}
function copyPath(relativePath, outDir) {
const source = path.join(ROOT_DIR, relativePath);
const target = path.join(outDir, relativePath);
if (!fs.existsSync(source)) {
throw new Error(`Required path is missing: ${relativePath}`);
}
fs.cpSync(source, target, {
recursive: true,
filter: shouldCopy
});
}
function writeSpaceFiles(outDir) {
fs.writeFileSync(path.join(outDir, 'README.md'), SPACE_README);
fs.writeFileSync(path.join(outDir, '.dockerignore'), [
'.git',
'node_modules',
'client/dist',
'dist',
'coverage',
'.vite',
'.cache',
'.codexmobile',
'.env',
'.env.*',
'*.log',
'.DS_Store',
''
].join('\n'));
}
function listOutput(outDir) {
const stack = ['.'];
const files = [];
while (stack.length > 0) {
const relative = stack.pop();
const absolute = path.join(outDir, relative);
for (const entry of fs.readdirSync(absolute, { withFileTypes: true })) {
const nextRelative = path.join(relative, entry.name);
if (entry.isDirectory()) {
stack.push(nextRelative);
} else {
files.push(nextRelative.replaceAll(path.sep, '/'));
}
}
}
return files.sort();
}
function main() {
const options = parseArgs(process.argv.slice(2));
assertSafeOutputDir(options.outDir);
fs.rmSync(options.outDir, { recursive: true, force: true });
fs.mkdirSync(options.outDir, { recursive: true });
for (const relativePath of COPY_PATHS) {
copyPath(relativePath, options.outDir);
}
writeSpaceFiles(options.outDir);
const files = listOutput(options.outDir);
console.log(`Prepared HuggingFace Docker Space at ${options.outDir}`);
console.log(`Files: ${files.length}`);
console.log('Next: push this directory to the target HuggingFace Space repository, then configure secrets in Space Settings.');
}
main();