mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-11-06 04:42:24 +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">
|
<div class="install-container">
|
||||||
<p class="install-label">Install from GitHub tarball</p>
|
<p class="install-label">Install from GitHub tarball</p>
|
||||||
<div class="install-code-wrapper">
|
<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)">
|
<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">
|
<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>
|
<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' });
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||||
fs.createReadStream(userConfigPath).pipe(res);
|
fs.createReadStream(userConfigPath).pipe(res);
|
||||||
return;
|
return;
|
||||||
} catch {
|
|
||||||
// Fall through
|
|
||||||
}
|
}
|
||||||
// If not found in CWD, fall through to serve from packageRoot (if it exists there)
|
// If not found in CWD, fall through to serve from packageRoot (if it exists there)
|
||||||
// or return empty object if missing?
|
// or return empty object if missing?
|
||||||
|
|||||||
+9
-278
@@ -16,196 +16,17 @@ import {
|
|||||||
DEFAULT_DASHBOARD_PORT,
|
DEFAULT_DASHBOARD_PORT,
|
||||||
VALID_CADENCES,
|
VALID_CADENCES,
|
||||||
KIND_APP_DATA,
|
KIND_APP_DATA,
|
||||||
|
RACE_CHECK_DELAY_MS,
|
||||||
} from './constants.mjs';
|
} from './constants.mjs';
|
||||||
import { cmdInit, cmdUpdate } from './ops.mjs';
|
import { cmdInit, cmdUpdate } from './ops.mjs';
|
||||||
import { getRoster as _getRoster } from './roster.mjs';
|
import { getRoster as _getRoster } from './roster.mjs';
|
||||||
import { queryLocks as _queryLocks, publishLock as _publishLock, parseLockEvent } from './lock-ops.mjs';
|
import { queryLocks as _queryLocks, publishLock as _publishLock, parseLockEvent } from './lock-ops.mjs';
|
||||||
import { cmdDashboard } from './dashboard.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 { ExitError } from './errors.mjs';
|
||||||
|
import { todayDateStr, nowUnix } from './utils.mjs';
|
||||||
|
|
||||||
useWebSocketImplementation(WebSocket);
|
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
|
// Re-export for backward compatibility/library usage
|
||||||
export { parseLockEvent, cmdDashboard, _queryLocks as queryLocks, _publishLock as publishLock };
|
export { parseLockEvent, cmdDashboard, _queryLocks as queryLocks, _publishLock as publishLock };
|
||||||
|
|
||||||
@@ -404,13 +225,17 @@ export async function cmdList(cadence, deps = {}) {
|
|||||||
|
|
||||||
const relays = getRelays();
|
const relays = getRelays();
|
||||||
const namespace = getNamespace();
|
const namespace = getNamespace();
|
||||||
const dateStr = todayDateStr();
|
const dateStr = getDateStr();
|
||||||
const cadences = cadence ? [cadence] : [...VALID_CADENCES];
|
const cadences = cadence ? [cadence] : [...VALID_CADENCES];
|
||||||
|
|
||||||
error(`Listing active locks: namespace=${namespace}, cadences=${cadences.join(', ')}`);
|
error(`Listing active locks: namespace=${namespace}, cadences=${cadences.join(', ')}`);
|
||||||
|
|
||||||
for (const c of cadences) {
|
const results = await Promise.all(
|
||||||
const locks = await queryLocks(relays, c, dateStr, namespace);
|
cadences.map(async (c) => {
|
||||||
|
const locks = await queryLocks(relays, c, dateStr, namespace);
|
||||||
|
return { c, locks };
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
for (const { c, locks } of results) {
|
for (const { c, locks } of results) {
|
||||||
log(`\n${'='.repeat(72)}`);
|
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) {
|
function parseArgs(argv) {
|
||||||
const args = { command: null, agent: null, cadence: null, dryRun: false, force: false, port: DEFAULT_DASHBOARD_PORT };
|
const args = { command: null, agent: null, cadence: null, dryRun: false, force: false, port: DEFAULT_DASHBOARD_PORT };
|
||||||
|
|||||||
Reference in new issue
Block a user