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:
google-labs-jules[bot]andPR0M3TH3AN committed 2026-02-14 17:45:21 +00:00
1 parent 6410008b6b
commit 6d072448e3
3 files changed
+10 -281

No files matched your search

+1 -1
View File
@@ -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>
-2
View File
@@ -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
View File
@@ -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 };