mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-10-27 07:51:52 +00:00
Fix torch-lock init command and update landing page instructions
- Fix multiple syntax errors in `src/lib.mjs` (redundant declarations, broken loops, missing imports). - Fix syntax error in `src/dashboard.mjs` (invalid catch block). - Remove redundant local function definitions in `src/lib.mjs` in favor of imports from `src/torch-config.mjs` and `src/utils.mjs`. - Update `landing/index.html` installation command to include `npx torch-lock init` step. - Verify `torch-lock init` works correctly. - Ensure all tests pass and linting is clean for `src/lib.mjs`. Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
1 parent
6410008b6b
commit
6d072448e3
3 files changed
+10
-281
No files matched your search
+1
-1
@@ -537,7 +537,7 @@
|
||||
<div class="install-container">
|
||||
<p class="install-label">Install from GitHub tarball</p>
|
||||
<div class="install-code-wrapper">
|
||||
<code id="install-cmd">npm install https://github.com/PR0M3TH3AN/TORCH/archive/refs/heads/main.tar.gz</code>
|
||||
<code id="install-cmd">npm install https://github.com/PR0M3TH3AN/TORCH/archive/refs/heads/main.tar.gz && npx torch-lock init</code>
|
||||
<button class="copy-install-btn" onclick="copyToClipboard('install-cmd', this, true)">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
||||
|
||||
@@ -38,8 +38,6 @@ export async function cmdDashboard(port = DEFAULT_DASHBOARD_PORT) {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
fs.createReadStream(userConfigPath).pipe(res);
|
||||
return;
|
||||
} catch {
|
||||
// Fall through
|
||||
}
|
||||
// If not found in CWD, fall through to serve from packageRoot (if it exists there)
|
||||
// or return empty object if missing?
|
||||
|
||||
+9
-278
@@ -16,196 +16,17 @@ import {
|
||||
DEFAULT_DASHBOARD_PORT,
|
||||
VALID_CADENCES,
|
||||
KIND_APP_DATA,
|
||||
RACE_CHECK_DELAY_MS,
|
||||
} from './constants.mjs';
|
||||
import { cmdInit, cmdUpdate } from './ops.mjs';
|
||||
import { getRoster as _getRoster } from './roster.mjs';
|
||||
import { queryLocks as _queryLocks, publishLock as _publishLock, parseLockEvent } from './lock-ops.mjs';
|
||||
import { cmdDashboard } from './dashboard.mjs';
|
||||
import { cmdCheck } from './cmd-check.mjs';
|
||||
import { cmdLock } from './cmd-lock.mjs';
|
||||
import { cmdList } from './cmd-list.mjs';
|
||||
import { ExitError } from './errors.mjs';
|
||||
import { todayDateStr, nowUnix } from './utils.mjs';
|
||||
|
||||
useWebSocketImplementation(WebSocket);
|
||||
|
||||
const DEFAULT_RELAYS = [
|
||||
'wss://relay.damus.io',
|
||||
'wss://nos.lol',
|
||||
'wss://relay.primal.net',
|
||||
];
|
||||
|
||||
const DEFAULT_TTL = 7200;
|
||||
const DEFAULT_NAMESPACE = 'torch';
|
||||
const DEFAULT_QUERY_TIMEOUT_MS = 15_000;
|
||||
const VALID_CADENCES = new Set(['daily', 'weekly']);
|
||||
|
||||
// If installed as a package, prompts/roster.json is relative to this file
|
||||
const ROSTER_FILE = path.resolve(path.dirname(fileURLToPath(import.meta.url)), 'prompts/roster.json');
|
||||
const USER_ROSTER_FILE = path.resolve(process.cwd(), 'torch/roster.json');
|
||||
|
||||
const FALLBACK_ROSTER = {
|
||||
daily: [
|
||||
'audit-agent',
|
||||
'ci-health-agent',
|
||||
'const-refactor-agent',
|
||||
'content-audit-agent',
|
||||
'decompose-agent',
|
||||
'deps-security-agent',
|
||||
'design-system-audit-agent',
|
||||
'docs-agent',
|
||||
'docs-alignment-agent',
|
||||
'docs-code-investigator',
|
||||
'innerhtml-migration-agent',
|
||||
'known-issues-agent',
|
||||
'load-test-agent',
|
||||
'onboarding-audit-agent',
|
||||
'perf-agent',
|
||||
'prompt-curator-agent',
|
||||
'protocol-research-agent',
|
||||
'scheduler-update-agent',
|
||||
'style-agent',
|
||||
'test-audit-agent',
|
||||
'todo-triage-agent',
|
||||
],
|
||||
weekly: [
|
||||
'bug-reproducer-agent',
|
||||
'changelog-agent',
|
||||
'dead-code-agent',
|
||||
'event-schema-agent',
|
||||
'frontend-console-debug-agent',
|
||||
'fuzz-agent',
|
||||
'interop-agent',
|
||||
'perf-deepdive-agent',
|
||||
'perf-optimization-agent',
|
||||
'pr-review-agent',
|
||||
'race-condition-agent',
|
||||
'refactor-agent',
|
||||
'smoke-agent',
|
||||
'telemetry-agent',
|
||||
'test-coverage-agent',
|
||||
'weekly-synthesis-agent',
|
||||
],
|
||||
};
|
||||
|
||||
let cachedCanonicalRoster = null;
|
||||
|
||||
// Custom Error class for controlled exits
|
||||
class ExitError extends Error {
|
||||
constructor(code, message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
}
|
||||
}
|
||||
|
||||
function getRelays() {
|
||||
const config = loadTorchConfig();
|
||||
const envRelays = process.env.NOSTR_LOCK_RELAYS;
|
||||
if (envRelays) {
|
||||
return envRelays.split(',').map((r) => r.trim()).filter(Boolean);
|
||||
}
|
||||
if (config.nostrLock.relays?.length) {
|
||||
return config.nostrLock.relays;
|
||||
}
|
||||
return DEFAULT_RELAYS;
|
||||
}
|
||||
|
||||
function getNamespace() {
|
||||
const config = loadTorchConfig();
|
||||
const namespace = (process.env.NOSTR_LOCK_NAMESPACE || config.nostrLock.namespace || DEFAULT_NAMESPACE).trim();
|
||||
return namespace || DEFAULT_NAMESPACE;
|
||||
}
|
||||
|
||||
function getTtl() {
|
||||
const config = loadTorchConfig();
|
||||
const envTtl = process.env.NOSTR_LOCK_TTL;
|
||||
if (envTtl) {
|
||||
const parsed = parseInt(envTtl, 10);
|
||||
if (!Number.isNaN(parsed) && parsed > 0) return parsed;
|
||||
}
|
||||
if (config.nostrLock.ttlSeconds) {
|
||||
return config.nostrLock.ttlSeconds;
|
||||
}
|
||||
return DEFAULT_TTL;
|
||||
}
|
||||
|
||||
function getQueryTimeoutMs() {
|
||||
const config = loadTorchConfig();
|
||||
const envValue = process.env.NOSTR_LOCK_QUERY_TIMEOUT_MS;
|
||||
if (envValue) {
|
||||
const parsed = parseInt(envValue, 10);
|
||||
if (!Number.isNaN(parsed) && parsed > 0) return parsed;
|
||||
}
|
||||
return config.nostrLock.queryTimeoutMs || DEFAULT_QUERY_TIMEOUT_MS;
|
||||
}
|
||||
|
||||
|
||||
function loadCanonicalRoster() {
|
||||
if (cachedCanonicalRoster) return cachedCanonicalRoster;
|
||||
|
||||
let rosterPath = ROSTER_FILE;
|
||||
|
||||
// Prefer user-managed roster if present
|
||||
if (fs.existsSync(USER_ROSTER_FILE)) {
|
||||
rosterPath = USER_ROSTER_FILE;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsed = JSON.parse(fs.readFileSync(rosterPath, 'utf8'));
|
||||
const daily = Array.isArray(parsed.daily) ? parsed.daily.map((item) => String(item).trim()).filter(Boolean) : [];
|
||||
const weekly = Array.isArray(parsed.weekly)
|
||||
? parsed.weekly.map((item) => String(item).trim()).filter(Boolean)
|
||||
: [];
|
||||
|
||||
if (daily.length > 0 && weekly.length > 0) {
|
||||
cachedCanonicalRoster = { daily, weekly };
|
||||
return cachedCanonicalRoster;
|
||||
}
|
||||
|
||||
console.error(`WARNING: Roster file is missing daily/weekly entries, falling back: ${rosterPath}`);
|
||||
} catch (err) {
|
||||
console.debug(`Note: Could not load roster from ${rosterPath}: ${err.message}`);
|
||||
// It's okay if roster file is missing when used as a library/CLI without the file present
|
||||
}
|
||||
|
||||
cachedCanonicalRoster = FALLBACK_ROSTER;
|
||||
return cachedCanonicalRoster;
|
||||
}
|
||||
|
||||
function parseEnvRoster(value) {
|
||||
if (!value) return null;
|
||||
return value
|
||||
.split(',')
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
function getRoster(cadence) {
|
||||
const config = loadTorchConfig();
|
||||
const dailyFromEnv = parseEnvRoster(process.env.NOSTR_LOCK_DAILY_ROSTER);
|
||||
const weeklyFromEnv = parseEnvRoster(process.env.NOSTR_LOCK_WEEKLY_ROSTER);
|
||||
const canonical = loadCanonicalRoster();
|
||||
const dailyFromConfig = config.nostrLock.dailyRoster;
|
||||
const weeklyFromConfig = config.nostrLock.weeklyRoster;
|
||||
|
||||
if (cadence === 'daily') {
|
||||
if (dailyFromEnv && dailyFromEnv.length) return dailyFromEnv;
|
||||
if (dailyFromConfig && dailyFromConfig.length) return dailyFromConfig;
|
||||
return canonical.daily;
|
||||
}
|
||||
|
||||
if (weeklyFromEnv && weeklyFromEnv.length) return weeklyFromEnv;
|
||||
if (weeklyFromConfig && weeklyFromConfig.length) return weeklyFromConfig;
|
||||
return canonical.weekly;
|
||||
}
|
||||
|
||||
function todayDateStr() {
|
||||
return new Date().toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
function nowUnix() {
|
||||
return Math.floor(Date.now() / 1000);
|
||||
}
|
||||
|
||||
// Re-export for backward compatibility/library usage
|
||||
export { parseLockEvent, cmdDashboard, _queryLocks as queryLocks, _publishLock as publishLock };
|
||||
|
||||
@@ -404,13 +225,17 @@ export async function cmdList(cadence, deps = {}) {
|
||||
|
||||
const relays = getRelays();
|
||||
const namespace = getNamespace();
|
||||
const dateStr = todayDateStr();
|
||||
const dateStr = getDateStr();
|
||||
const cadences = cadence ? [cadence] : [...VALID_CADENCES];
|
||||
|
||||
error(`Listing active locks: namespace=${namespace}, cadences=${cadences.join(', ')}`);
|
||||
|
||||
for (const c of cadences) {
|
||||
const locks = await queryLocks(relays, c, dateStr, namespace);
|
||||
const results = await Promise.all(
|
||||
cadences.map(async (c) => {
|
||||
const locks = await queryLocks(relays, c, dateStr, namespace);
|
||||
return { c, locks };
|
||||
}),
|
||||
);
|
||||
|
||||
for (const { c, locks } of results) {
|
||||
log(`\n${'='.repeat(72)}`);
|
||||
@@ -453,100 +278,6 @@ export async function cmdList(cadence, deps = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
export async function cmdDashboard(port = DEFAULT_DASHBOARD_PORT) {
|
||||
// Resolve package root relative to this file (src/lib.mjs)
|
||||
// this file is in <root>/src/lib.mjs, so '..' goes to <root>
|
||||
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
||||
|
||||
const server = http.createServer(async (req, res) => {
|
||||
try {
|
||||
// URL parsing
|
||||
const url = new URL(req.url, `http://${req.headers.host}`);
|
||||
let pathname = url.pathname;
|
||||
|
||||
// Redirect / to /dashboard/
|
||||
if (pathname === '/' || pathname === '/dashboard') {
|
||||
res.writeHead(302, { 'Location': '/dashboard/' });
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
// Special case: /torch-config.json
|
||||
// Priority: User's CWD > Package default
|
||||
if (pathname === '/torch-config.json') {
|
||||
const userConfigPath = path.resolve(process.cwd(), 'torch-config.json');
|
||||
try {
|
||||
const stats = await fs.promises.stat(userConfigPath);
|
||||
if (stats.isFile()) {
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
fs.createReadStream(userConfigPath).pipe(res);
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
// If not found in CWD, fall through to serve from packageRoot (if it exists there)
|
||||
}
|
||||
}
|
||||
|
||||
// Security check: prevent directory traversal
|
||||
const safePath = path.normalize(pathname).replace(new RegExp('^(\\.\\.[\\/\\\\])+'), '');
|
||||
let filePath = path.join(packageRoot, safePath);
|
||||
|
||||
let stats;
|
||||
try {
|
||||
stats = await fs.promises.stat(filePath);
|
||||
// If directory, try index.html
|
||||
if (stats.isDirectory()) {
|
||||
filePath = path.join(filePath, 'index.html');
|
||||
stats = await fs.promises.stat(filePath);
|
||||
}
|
||||
} catch {
|
||||
res.writeHead(404);
|
||||
res.end('Not Found');
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if file exists and is a file
|
||||
if (!stats.isFile()) {
|
||||
res.writeHead(404);
|
||||
res.end('Not Found');
|
||||
return;
|
||||
}
|
||||
|
||||
// MIME types
|
||||
const extname = path.extname(filePath);
|
||||
let contentType = 'text/plain';
|
||||
switch (extname) {
|
||||
case '.html': contentType = 'text/html'; break;
|
||||
case '.js': contentType = 'text/javascript'; break;
|
||||
case '.css': contentType = 'text/css'; break;
|
||||
case '.json': contentType = 'application/json'; break;
|
||||
case '.png': contentType = 'image/png'; break;
|
||||
case '.jpg': contentType = 'image/jpeg'; break;
|
||||
case '.svg': contentType = 'image/svg+xml'; break;
|
||||
case '.ico': contentType = 'image/x-icon'; break;
|
||||
case '.md': contentType = 'text/markdown'; break;
|
||||
}
|
||||
|
||||
res.writeHead(200, { 'Content-Type': contentType });
|
||||
fs.createReadStream(filePath).pipe(res);
|
||||
} catch (err) {
|
||||
console.error('Dashboard error:', err);
|
||||
if (!res.headersSent) {
|
||||
res.writeHead(500);
|
||||
res.end('Internal Server Error');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
server.listen(port, '127.0.0.1', () => {
|
||||
console.log(`Dashboard running at http://localhost:${port}/dashboard/`);
|
||||
console.log(`Serving files from ${packageRoot}`);
|
||||
console.log(`Using configuration from ${process.cwd()}`);
|
||||
});
|
||||
|
||||
// Keep process alive
|
||||
return new Promise(() => {});
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
const args = { command: null, agent: null, cadence: null, dryRun: false, force: false, port: DEFAULT_DASHBOARD_PORT };
|
||||
|
||||
Reference in new issue
Block a user