Merge branch 'main' into test-improvement-get-relevant-memories-15158310533650216935

This commit is contained in:
thePR0M3TH3AN authored and GitHub committed 2026-02-17 14:26:54 -05:00
commit fb558b0b4d
20 files changed
+1159 -539

No files matched your search

+5 -5
View File
@@ -8,10 +8,10 @@ import { queryLocks } from './lock-ops.mjs';
import { todayDateStr } from './utils.mjs';
export async function cmdCheck(cadence) {
const relays = getRelays();
const namespace = getNamespace();
const relays = await getRelays();
const namespace = await getNamespace();
const dateStr = todayDateStr();
const config = loadTorchConfig();
const config = await loadTorchConfig();
const pausedAgents = (cadence === 'daily' ? config.scheduler.paused.daily : config.scheduler.paused.weekly) || [];
console.error(`Checking locks: namespace=${namespace}, cadence=${cadence}, date=${dateStr}`);
@@ -20,9 +20,9 @@ export async function cmdCheck(cadence) {
console.error(`Paused agents: ${pausedAgents.join(', ')}`);
}
const locks = await queryLocks(relays, cadence, dateStr, namespace);
const locks = await queryLocksFn(relays, cadence, dateStr, namespace);
const lockedAgents = [...new Set(locks.map((l) => l.agent).filter(Boolean))];
const roster = getRoster(cadence);
const roster = await getRoster(cadence);
const excludedAgents = [...new Set([...lockedAgents, ...pausedAgents])];
const unknownLockedAgents = lockedAgents.filter((agent) => !roster.includes(agent));
+3 -3
View File
@@ -7,8 +7,8 @@ import { queryLocks } from './lock-ops.mjs';
import { todayDateStr, nowUnix } from './utils.mjs';
export async function cmdList(cadence) {
const relays = getRelays();
const namespace = getNamespace();
const relays = await getRelays();
const namespace = await getNamespace();
const dateStr = todayDateStr();
const cadences = cadence ? [cadence] : ['daily', 'weekly'];
@@ -47,7 +47,7 @@ export async function cmdList(cadence) {
);
}
const roster = getRoster(c);
const roster = await getRoster(c);
const lockedAgents = new Set(locks.map((l) => l.agent).filter(Boolean));
const unknownLockedAgents = [...lockedAgents].filter((agent) => !roster.includes(agent));
const available = roster.filter((a) => !lockedAgents.has(a));
+4 -4
View File
@@ -14,10 +14,10 @@ import { todayDateStr, nowUnix } from './utils.mjs';
import { ExitError } from './errors.mjs';
export async function cmdLock(agent, cadence, dryRun = false) {
const relays = getRelays();
const namespace = getNamespace();
const relays = await getRelays();
const namespace = await getNamespace();
const dateStr = todayDateStr();
const ttl = getTtl();
const ttl = await getTtl();
const now = nowUnix();
const expiresAt = now + ttl;
@@ -25,7 +25,7 @@ export async function cmdLock(agent, cadence, dryRun = false) {
console.error(`TTL: ${ttl}s, expires: ${new Date(expiresAt * 1000).toISOString()}`);
console.error(`Relays: ${relays.join(', ')}`);
const roster = getRoster(cadence);
const roster = await getRoster(cadence);
if (!roster.includes(agent)) {
console.error(`ERROR: agent "${agent}" is not in the ${cadence} roster`);
console.error(`Allowed ${cadence} agents: ${roster.join(', ')}`);
+23 -14
View File
@@ -22,9 +22,10 @@ export async function cmdDashboard(port = DEFAULT_DASHBOARD_PORT, host = '127.0.
// Resolve package root relative to this file (src/dashboard.mjs)
// this file is in <root>/src/dashboard.mjs, so '..' goes to <root>
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
const auth = getDashboardAuth();
const auth = await getDashboardAuth();
const server = http.createServer(async (req, res) => {
try {
// Basic Auth check
if (auth) {
const authHeader = req.headers.authorization;
@@ -181,20 +182,28 @@ export async function cmdDashboard(port = DEFAULT_DASHBOARD_PORT, host = '127.0.
res.writeHead(200, { 'Content-Type': contentType });
fs.createReadStream(filePath).pipe(res);
});
server.listen(port, host, () => {
const listenUrl = `http://${host === '0.0.0.0' ? 'localhost' : host}:${port}/dashboard/`;
console.log(`Dashboard running at ${listenUrl}`);
if (auth) {
console.log('Authentication: enabled (Basic Auth)');
} else {
console.warn('Authentication: DISABLED (Dashboard is public)');
} catch (err) {
console.error('Dashboard Server Error:', err);
if (!res.headersSent) {
res.writeHead(500);
res.end('Internal Server Error');
}
}
console.log(`Serving files from ${packageRoot}`);
console.log(`Using configuration from ${process.cwd()}`);
});
// Keep process alive
return server;
return new Promise((resolve, reject) => {
server.listen(port, host, () => {
const listenUrl = `http://${host === '0.0.0.0' ? 'localhost' : host}:${port}/dashboard/`;
console.log(`Dashboard running at ${listenUrl}`);
if (auth) {
console.log('Authentication: enabled (Basic Auth)');
} else {
console.warn('Authentication: DISABLED (Dashboard is public)');
}
console.log(`Serving files from ${packageRoot}`);
console.log(`Using configuration from ${process.cwd()}`);
resolve(server);
});
server.on('error', reject);
});
}
+15 -15
View File
@@ -75,10 +75,10 @@ export async function cmdCheck(cadence, deps = {}) {
quiet = false,
} = deps;
const relays = getRelays();
const namespace = getNamespace();
const relays = await getRelays();
const namespace = await getNamespace();
const dateStr = getDateStr();
const config = loadTorchConfig();
const config = await loadTorchConfig();
const pausedAgents = config.scheduler.paused[cadence] || [];
if (!quiet) {
@@ -99,7 +99,7 @@ export async function cmdCheck(cadence, deps = {}) {
const locks = await queryLocks(relays, cadence, dateStr, namespace);
const lockedAgents = [...new Set(locks.map((l) => l.agent).filter(Boolean))];
const roster = getRoster(cadence);
const roster = await getRoster(cadence);
const rosterSet = new Set(roster);
const excludedAgentsSet = new Set([...lockedAgents, ...pausedAgents, ...completedAgents]);
@@ -180,11 +180,11 @@ export async function cmdLock(agent, cadence, optionsOrDryRun = false, deps = {}
error = console.error
} = deps;
const relays = getRelays();
const namespace = getNamespace();
const hashtag = getHashtag();
const relays = await getRelays();
const namespace = await getNamespace();
const hashtag = await getHashtag();
const dateStr = getDateStr();
const ttl = getTtl();
const ttl = await getTtl();
const now = nowUnix();
const expiresAt = now + ttl;
@@ -193,7 +193,7 @@ export async function cmdLock(agent, cadence, optionsOrDryRun = false, deps = {}
error(`TTL: ${ttl}s, expires: ${new Date(expiresAt * 1000).toISOString()}`);
error(`Relays: ${relays.join(', ')}`);
const roster = getRoster(cadence);
const roster = await getRoster(cadence);
if (!roster.includes(agent)) {
error(`ERROR: agent "${agent}" is not in the ${cadence} roster`);
error(`Allowed ${cadence} agents: ${roster.join(', ')}`);
@@ -319,8 +319,8 @@ export async function cmdList(cadence, deps = {}) {
error = console.error
} = deps;
const relays = getRelays();
const namespace = getNamespace();
const relays = await getRelays();
const namespace = await getNamespace();
const dateStr = getDateStr();
const cadences = cadence ? [cadence] : [...VALID_CADENCES];
@@ -365,7 +365,7 @@ export async function cmdList(cadence, deps = {}) {
);
}
const roster = getRoster(c);
const roster = await getRoster(c);
const rosterSet = new Set(roster);
const lockedAgents = new Set(locks.map((l) => l.agent).filter(Boolean));
const unknownLockedAgents = [...lockedAgents].filter((agent) => !rosterSet.has(agent));
@@ -413,9 +413,9 @@ export async function cmdComplete(agent, cadence, optionsOrDryRun = false, deps
error = console.error
} = deps;
const relays = getRelays();
const namespace = getNamespace();
const hashtag = getHashtag();
const relays = await getRelays();
const namespace = await getNamespace();
const hashtag = await getHashtag();
const dateStr = getDateStr();
const now = nowUnix();
+7 -7
View File
@@ -306,11 +306,11 @@ export async function queryLocks(relays, cadence, dateStr, namespace, deps = {})
const pool = poolFactory();
const tagFilter = `${namespace}-lock-${cadence}-${dateStr}`;
const queryTimeoutMs = getQueryTimeoutMsFn();
const fallbackRelays = getRelayFallbacksFn().filter((relay) => !relays.includes(relay));
const queryTimeoutMs = await getQueryTimeoutMsFn();
const fallbackRelays = (await getRelayFallbacksFn()).filter((relay) => !relays.includes(relay));
const healthConfig = buildRelayHealthConfig({
...deps,
minActiveRelayPool: getMinActiveRelayPoolFn(),
minActiveRelayPool: await getMinActiveRelayPoolFn(),
});
const runQuery = async (relaySet, phase) => {
@@ -419,13 +419,13 @@ export async function publishLock(relays, event, deps = {}) {
const attemptId = diagnostics.attemptId || process.env.SCHEDULER_LOCK_ATTEMPT_ID || '1';
const pool = poolFactory();
const publishTimeoutMs = getPublishTimeoutMsFn();
const minSuccesses = getMinSuccessfulRelayPublishesFn();
const fallbackRelays = getRelayFallbacksFn().filter((relay) => !relays.includes(relay));
const publishTimeoutMs = await getPublishTimeoutMsFn();
const minSuccesses = await getMinSuccessfulRelayPublishesFn();
const fallbackRelays = (await getRelayFallbacksFn()).filter((relay) => !relays.includes(relay));
const maxAttempts = Math.max(1, Math.floor(retryAttempts));
const healthConfig = buildRelayHealthConfig({
...deps,
minActiveRelayPool: getMinActiveRelayPoolFn(),
minActiveRelayPool: await getMinActiveRelayPoolFn(),
});
const publishOnce = async () => {
+45
View File
@@ -0,0 +1,45 @@
import { loadTorchConfig, _resetTorchConfigCache } from '../torch-config.mjs';
import fs from 'node:fs';
import path from 'node:path';
const configPath = path.resolve(process.cwd(), 'torch-config.json');
const originalConfig = fs.existsSync(configPath) ? fs.readFileSync(configPath) : null;
// Ensure a config file exists for benchmark
if (!fs.existsSync(configPath)) {
fs.writeFileSync(configPath, JSON.stringify({
nostrLock: {
relays: ["wss://relay.damus.io"],
namespace: "test-ns"
}
}, null, 2));
}
const ITERATIONS = 1000;
const start = process.hrtime.bigint();
async function run() {
for (let i = 0; i < ITERATIONS; i++) {
_resetTorchConfigCache();
try {
const res = await loadTorchConfig();
// Use res to prevent dead code elimination if JIT is smart (though side effects exist)
if (!res) throw new Error("Failed");
} catch (e) {
// ignore
}
}
const end = process.hrtime.bigint();
const duration = Number(end - start) / 1e6; // ms
console.log(`[ASYNC] Loaded config ${ITERATIONS} times in ${duration.toFixed(2)}ms`);
console.log(`[ASYNC] Average: ${(duration / ITERATIONS).toFixed(4)}ms per load`);
// Cleanup
if (!originalConfig && fs.existsSync(configPath)) {
fs.unlinkSync(configPath);
}
}
run();
+10 -3
View File
@@ -196,15 +196,22 @@ async function appendHistory(historyPath, entry) {
export async function runRelayHealthCheck(options = {}) {
const {
cadence = 'daily',
relays = getRelays(),
namespace = getNamespace(),
timeoutMs = 6000,
historyPath = path.resolve(process.cwd(), 'task-logs', 'relay-health', `${cadence}.jsonl`),
allRelaysDownMinutes = 10,
minSuccessRate = 0.7,
windowMinutes = 60,
} = options;
let {
relays,
namespace,
historyPath,
} = options;
if (!relays) relays = await getRelays();
if (!namespace) namespace = await getNamespace();
if (!historyPath) historyPath = path.resolve(process.cwd(), 'task-logs', 'relay-health', `${cadence}.jsonl`);
const relayResults = [];
for (const relay of relays) {
const relayResult = {
+2 -2
View File
@@ -94,8 +94,8 @@ function parseEnvRoster(value) {
.filter(Boolean);
}
export function getRoster(cadence) {
const config = loadTorchConfig();
export async function getRoster(cadence) {
const config = await loadTorchConfig();
const dailyFromEnv = parseEnvRoster(process.env.NOSTR_LOCK_DAILY_ROSTER);
const weeklyFromEnv = parseEnvRoster(process.env.NOSTR_LOCK_WEEKLY_ROSTER);
const canonical = loadCanonicalRoster();
+28 -26
View File
@@ -127,18 +127,20 @@ export function _resetTorchConfigCache() {
cachedConfig = null;
}
export function loadTorchConfig(fileSystem = fs) {
export async function loadTorchConfig(fileSystem = fs) {
if (cachedConfig) return cachedConfig;
const configPath = getTorchConfigPath();
let raw = {};
if (fileSystem.existsSync(configPath)) {
try {
raw = JSON.parse(fileSystem.readFileSync(configPath, 'utf8'));
} catch (err) {
try {
const content = await fileSystem.promises.readFile(configPath, 'utf8');
raw = JSON.parse(content);
} catch (err) {
if (err.code !== 'ENOENT') {
throw new Error(`Failed to parse ${configPath}: ${err.message}`, { cause: err });
}
// If file missing (ENOENT), we use empty object defaults
}
cachedConfig = parseTorchConfig(raw, configPath);
@@ -199,8 +201,8 @@ function resolveIntegerConfig(envKey, configValue, defaultValue, validator, conf
return value;
}
export function getRelays() {
const config = loadTorchConfig();
export async function getRelays() {
const config = await loadTorchConfig();
const envRelays = process.env.NOSTR_LOCK_RELAYS;
if (envRelays) {
return parseEnvRelayList(envRelays, 'NOSTR_LOCK_RELAYS');
@@ -214,8 +216,8 @@ export function getRelays() {
return DEFAULT_RELAYS;
}
export function getRelayFallbacks() {
const config = loadTorchConfig();
export async function getRelayFallbacks() {
const config = await loadTorchConfig();
const envRelays = process.env.NOSTR_LOCK_RELAY_FALLBACKS;
if (envRelays) {
return parseEnvRelayList(envRelays, 'NOSTR_LOCK_RELAY_FALLBACKS');
@@ -223,14 +225,14 @@ export function getRelayFallbacks() {
return config.nostrLock.relayFallbacks || [];
}
export function getNamespace() {
const config = loadTorchConfig();
export async function getNamespace() {
const config = await loadTorchConfig();
const namespace = (process.env.NOSTR_LOCK_NAMESPACE || config.nostrLock.namespace || DEFAULT_NAMESPACE).trim();
return namespace || DEFAULT_NAMESPACE;
}
export function getTtl() {
const config = loadTorchConfig();
export async function getTtl() {
const config = await loadTorchConfig();
const envTtl = process.env.NOSTR_LOCK_TTL;
if (envTtl) {
const parsed = parseInt(envTtl, 10);
@@ -242,8 +244,8 @@ export function getTtl() {
return DEFAULT_TTL;
}
export function getQueryTimeoutMs() {
const config = loadTorchConfig();
export async function getQueryTimeoutMs() {
const config = await loadTorchConfig();
return resolveIntegerConfig(
'NOSTR_LOCK_QUERY_TIMEOUT_MS',
config.nostrLock.queryTimeoutMs,
@@ -253,8 +255,8 @@ export function getQueryTimeoutMs() {
);
}
export function getPublishTimeoutMs() {
const config = loadTorchConfig();
export async function getPublishTimeoutMs() {
const config = await loadTorchConfig();
return resolveIntegerConfig(
'NOSTR_LOCK_PUBLISH_TIMEOUT_MS',
config.nostrLock.publishTimeoutMs,
@@ -264,8 +266,8 @@ export function getPublishTimeoutMs() {
);
}
export function getMinSuccessfulRelayPublishes() {
const config = loadTorchConfig();
export async function getMinSuccessfulRelayPublishes() {
const config = await loadTorchConfig();
return resolveIntegerConfig(
'NOSTR_LOCK_MIN_SUCCESSFUL_PUBLISHES',
config.nostrLock.minSuccessfulRelayPublishes,
@@ -275,8 +277,8 @@ export function getMinSuccessfulRelayPublishes() {
);
}
export function getMinActiveRelayPool() {
const config = loadTorchConfig();
export async function getMinActiveRelayPool() {
const config = await loadTorchConfig();
return resolveIntegerConfig(
'NOSTR_LOCK_MIN_ACTIVE_RELAY_POOL',
config.nostrLock.minActiveRelayPool,
@@ -286,8 +288,8 @@ export function getMinActiveRelayPool() {
);
}
export function getHashtag() {
const config = loadTorchConfig();
export async function getHashtag() {
const config = await loadTorchConfig();
const envValue = process.env.NOSTR_LOCK_HASHTAG;
if (envValue) {
return envValue.trim();
@@ -295,12 +297,12 @@ export function getHashtag() {
if (config.dashboard.hashtag) {
return config.dashboard.hashtag;
}
const namespace = getNamespace();
const namespace = await getNamespace();
return `${namespace}-agent-lock`;
}
export function getDashboardAuth() {
const config = loadTorchConfig();
export async function getDashboardAuth() {
const config = await loadTorchConfig();
const envValue = process.env.TORCH_DASHBOARD_AUTH;
if (envValue) {
return envValue.trim();