mirror of
https://github.com/PR0M3TH3AN/TORCH.git
synced 2026-10-27 07:51:52 +00:00
Merge pull request #9 from PR0M3TH3AN/codex/add-user-configurable-settings-in-torch-config
feat: add torch-config support for per-project nostr namespace, scheduler defaults, and dashboard views
This commit is contained in:
8 files changed
+258
-22
No files matched your search
@@ -8,11 +8,27 @@ From the repository root:
|
||||
|
||||
```bash
|
||||
npm install
|
||||
cp torch-config.json /path/to/your-project/torch-config.json # optional template step when vendoring TORCH
|
||||
npm run lock:check:daily
|
||||
AGENT_PLATFORM=codex npm run lock:lock -- --agent docs-agent --cadence daily
|
||||
npm run lock:list
|
||||
```
|
||||
|
||||
### Project configuration (`torch-config.json`)
|
||||
|
||||
Create a `torch-config.json` file in your project root (or set `TORCH_CONFIG_PATH`) to override defaults per repo:
|
||||
|
||||
- `nostrLock.namespace` (d-tag/tag namespace)
|
||||
- `nostrLock.relays`
|
||||
- `nostrLock.ttlSeconds`
|
||||
- `nostrLock.queryTimeoutMs`
|
||||
- `nostrLock.dailyRoster` / `nostrLock.weeklyRoster`
|
||||
- `dashboard.defaultCadenceView` (`daily`, `weekly`, or `all`)
|
||||
- `dashboard.defaultStatusView` (`active` or `all`)
|
||||
- `scheduler.firstPromptByCadence.daily` / `.weekly`
|
||||
|
||||
Default first-run behavior is configured so daily scheduling starts with `scheduler-update-agent`.
|
||||
|
||||
Optional dashboard:
|
||||
|
||||
```bash
|
||||
@@ -48,8 +64,10 @@ Declared in `package.json` and pinned:
|
||||
- `NOSTR_LOCK_NAMESPACE`
|
||||
- `NOSTR_LOCK_RELAYS`
|
||||
- `NOSTR_LOCK_TTL`
|
||||
- `NOSTR_LOCK_QUERY_TIMEOUT_MS`
|
||||
- `NOSTR_LOCK_DAILY_ROSTER`
|
||||
- `NOSTR_LOCK_WEEKLY_ROSTER`
|
||||
- `TORCH_CONFIG_PATH`
|
||||
- `AGENT_PLATFORM`
|
||||
|
||||
## Roster precedence
|
||||
@@ -57,8 +75,9 @@ Declared in `package.json` and pinned:
|
||||
The lock CLI resolves roster names in this order:
|
||||
|
||||
1. `NOSTR_LOCK_DAILY_ROSTER` / `NOSTR_LOCK_WEEKLY_ROSTER` (comma-separated env overrides).
|
||||
2. `src/prompts/roster.json` (`daily` / `weekly` canonical scheduler roster).
|
||||
3. Built-in fallback roster (used only if `src/prompts/roster.json` is unreadable).
|
||||
2. `torch-config.json` (`nostrLock.dailyRoster` / `nostrLock.weeklyRoster`).
|
||||
3. `src/prompts/roster.json` (`daily` / `weekly` canonical scheduler roster).
|
||||
4. Built-in fallback roster (used only if `src/prompts/roster.json` is unreadable).
|
||||
|
||||
`lock --agent` validates names against the resolved cadence roster, and `check`/`list` report lock events whose agent names do not match scheduler roster entries exactly.
|
||||
|
||||
|
||||
+62
-10
@@ -157,6 +157,8 @@
|
||||
const DASHBOARD_DEFAULTS = {
|
||||
namespace: 'torch',
|
||||
hashtag: '',
|
||||
defaultCadenceView: 'daily',
|
||||
defaultStatusView: 'active',
|
||||
relays: [
|
||||
'wss://relay.damus.io',
|
||||
'wss://nos.lol',
|
||||
@@ -164,13 +166,47 @@
|
||||
],
|
||||
};
|
||||
|
||||
function parseDashboardConfig() {
|
||||
function normalizeCadence(value) {
|
||||
const normalized = String(value || '').trim().toLowerCase();
|
||||
return normalized === 'daily' || normalized === 'weekly' || normalized === 'all'
|
||||
? normalized
|
||||
: DASHBOARD_DEFAULTS.defaultCadenceView;
|
||||
}
|
||||
|
||||
function normalizeStatus(value) {
|
||||
const normalized = String(value || '').trim().toLowerCase();
|
||||
return normalized === 'active' || normalized === 'all'
|
||||
? normalized
|
||||
: DASHBOARD_DEFAULTS.defaultStatusView;
|
||||
}
|
||||
|
||||
async function loadTorchConfigFile() {
|
||||
const configCandidates = ['../torch-config.json', './torch-config.json'];
|
||||
|
||||
for (const path of configCandidates) {
|
||||
try {
|
||||
const response = await fetch(path, { cache: 'no-store' });
|
||||
if (!response.ok) continue;
|
||||
return await response.json();
|
||||
} catch {
|
||||
// optional config, ignore
|
||||
}
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
function parseDashboardConfig(torchConfig = {}) {
|
||||
const params = new URLSearchParams(window.location.search);
|
||||
const dashboardConfig = torchConfig.dashboard || {};
|
||||
const lockConfig = torchConfig.nostrLock || {};
|
||||
|
||||
const configuredNamespace =
|
||||
(dashboardConfig.namespace || lockConfig.namespace || DASHBOARD_DEFAULTS.namespace);
|
||||
const namespace =
|
||||
(params.get('namespace') || DASHBOARD_DEFAULTS.namespace).trim() ||
|
||||
DASHBOARD_DEFAULTS.namespace;
|
||||
(params.get('namespace') || configuredNamespace).trim() || DASHBOARD_DEFAULTS.namespace;
|
||||
const hashtag =
|
||||
(params.get('hashtag') || DASHBOARD_DEFAULTS.hashtag).trim() ||
|
||||
(params.get('hashtag') || dashboardConfig.hashtag || DASHBOARD_DEFAULTS.hashtag).trim() ||
|
||||
`${namespace}-agent-lock`;
|
||||
const relaysParam = (params.get('relays') || '').trim();
|
||||
const relays = relaysParam
|
||||
@@ -178,22 +214,26 @@
|
||||
.split(',')
|
||||
.map((relay) => relay.trim())
|
||||
.filter(Boolean)
|
||||
: [...DASHBOARD_DEFAULTS.relays];
|
||||
: [...(dashboardConfig.relays || lockConfig.relays || DASHBOARD_DEFAULTS.relays)];
|
||||
const defaultCadenceView = normalizeCadence(dashboardConfig.defaultCadenceView);
|
||||
const defaultStatusView = normalizeStatus(dashboardConfig.defaultStatusView);
|
||||
|
||||
return {
|
||||
namespace,
|
||||
hashtag,
|
||||
relays,
|
||||
defaultCadenceView,
|
||||
defaultStatusView,
|
||||
};
|
||||
}
|
||||
|
||||
const DASHBOARD_CONFIG = parseDashboardConfig();
|
||||
let DASHBOARD_CONFIG = parseDashboardConfig({});
|
||||
|
||||
const RELAYS = DASHBOARD_CONFIG.relays;
|
||||
const D_TAG_PREFIX = `${DASHBOARD_CONFIG.namespace}-lock/`;
|
||||
let RELAYS = DASHBOARD_CONFIG.relays;
|
||||
let D_TAG_PREFIX = `${DASHBOARD_CONFIG.namespace}-lock/`;
|
||||
|
||||
const LOCK_EVENT_KIND = 30078;
|
||||
const HASHTAG = DASHBOARD_CONFIG.hashtag;
|
||||
let HASHTAG = DASHBOARD_CONFIG.hashtag;
|
||||
|
||||
function applyConfigToHelpText() {
|
||||
const byId = (id) => document.getElementById(id);
|
||||
@@ -646,6 +686,9 @@
|
||||
function init() {
|
||||
if (!isAlive()) return;
|
||||
|
||||
cadenceFilter.value = DASHBOARD_CONFIG.defaultCadenceView;
|
||||
statusFilter.value = DASHBOARD_CONFIG.defaultStatusView;
|
||||
|
||||
applyConfigToHelpText();
|
||||
|
||||
for (const url of RELAYS) {
|
||||
@@ -653,7 +696,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
async function bootstrap() {
|
||||
const torchConfig = await loadTorchConfigFile();
|
||||
DASHBOARD_CONFIG = parseDashboardConfig(torchConfig);
|
||||
RELAYS = DASHBOARD_CONFIG.relays;
|
||||
D_TAG_PREFIX = `${DASHBOARD_CONFIG.namespace}-lock/`;
|
||||
HASHTAG = DASHBOARD_CONFIG.hashtag;
|
||||
init();
|
||||
}
|
||||
|
||||
bootstrap();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+22
-2
@@ -31,17 +31,37 @@ node src/nostr-lock.mjs list
|
||||
- `NOSTR_LOCK_NAMESPACE`
|
||||
- `NOSTR_LOCK_RELAYS`
|
||||
- `NOSTR_LOCK_TTL`
|
||||
- `NOSTR_LOCK_QUERY_TIMEOUT_MS`
|
||||
- `NOSTR_LOCK_DAILY_ROSTER`
|
||||
- `NOSTR_LOCK_WEEKLY_ROSTER`
|
||||
- `TORCH_CONFIG_PATH`
|
||||
- `AGENT_PLATFORM`
|
||||
|
||||
## torch-config.json
|
||||
|
||||
You can configure TORCH per repository with a root-level `torch-config.json` file.
|
||||
|
||||
Common settings:
|
||||
|
||||
- `nostrLock.namespace` — namespace prefix used in d-tags and hashtags.
|
||||
- `nostrLock.relays` — relay list for check/lock/list operations.
|
||||
- `nostrLock.ttlSeconds` — default lock TTL.
|
||||
- `nostrLock.queryTimeoutMs` — relay query timeout.
|
||||
- `nostrLock.dailyRoster` / `nostrLock.weeklyRoster` — optional per-project roster overrides.
|
||||
- `dashboard.defaultCadenceView` — default dashboard view (`daily`, `weekly`, `all`).
|
||||
- `dashboard.defaultStatusView` — default dashboard status filter (`active`, `all`).
|
||||
- `scheduler.firstPromptByCadence.daily` / `.weekly` — first-run scheduler starting agent.
|
||||
|
||||
Default first-run daily scheduler prompt is `scheduler-update-agent`.
|
||||
|
||||
## Roster precedence
|
||||
|
||||
The lock CLI resolves roster names in this order:
|
||||
|
||||
1. `NOSTR_LOCK_DAILY_ROSTER` / `NOSTR_LOCK_WEEKLY_ROSTER` (comma-separated env overrides).
|
||||
2. `src/prompts/roster.json` (`daily` / `weekly` canonical scheduler roster).
|
||||
3. Built-in fallback roster (used only if `src/prompts/roster.json` is unreadable).
|
||||
2. `torch-config.json` (`nostrLock.dailyRoster` / `nostrLock.weeklyRoster`).
|
||||
3. `src/prompts/roster.json` (`daily` / `weekly` canonical scheduler roster).
|
||||
4. Built-in fallback roster (used only if `src/prompts/roster.json` is unreadable).
|
||||
|
||||
`lock --agent` validates names against the resolved cadence roster, and `check`/`list` report lock events whose agent names do not match scheduler roster entries exactly.
|
||||
|
||||
|
||||
+35
-5
@@ -10,6 +10,7 @@ import { generateSecretKey, getPublicKey, finalizeEvent } from 'nostr-tools/pure
|
||||
import { SimplePool } from 'nostr-tools/pool';
|
||||
import { useWebSocketImplementation } from 'nostr-tools/relay';
|
||||
import WebSocket from 'ws';
|
||||
import { loadTorchConfig } from './torch-config.mjs';
|
||||
|
||||
useWebSocketImplementation(WebSocket);
|
||||
|
||||
@@ -21,7 +22,7 @@ const DEFAULT_RELAYS = [
|
||||
|
||||
const DEFAULT_TTL = 7200;
|
||||
const DEFAULT_NAMESPACE = 'torch';
|
||||
const QUERY_TIMEOUT_MS = 15_000;
|
||||
const DEFAULT_QUERY_TIMEOUT_MS = 15_000;
|
||||
const VALID_CADENCES = new Set(['daily', 'weekly']);
|
||||
|
||||
const ROSTER_FILE = path.resolve(path.dirname(fileURLToPath(import.meta.url)), 'prompts/roster.json');
|
||||
@@ -72,27 +73,46 @@ const FALLBACK_ROSTER = {
|
||||
let cachedCanonicalRoster = null;
|
||||
|
||||
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 namespace = (process.env.NOSTR_LOCK_NAMESPACE || DEFAULT_NAMESPACE).trim();
|
||||
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;
|
||||
@@ -127,15 +147,22 @@ function parseEnvRoster(value) {
|
||||
}
|
||||
|
||||
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') {
|
||||
return dailyFromEnv && dailyFromEnv.length ? dailyFromEnv : canonical.daily;
|
||||
if (dailyFromEnv && dailyFromEnv.length) return dailyFromEnv;
|
||||
if (dailyFromConfig && dailyFromConfig.length) return dailyFromConfig;
|
||||
return canonical.daily;
|
||||
}
|
||||
|
||||
return weeklyFromEnv && weeklyFromEnv.length ? weeklyFromEnv : canonical.weekly;
|
||||
if (weeklyFromEnv && weeklyFromEnv.length) return weeklyFromEnv;
|
||||
if (weeklyFromConfig && weeklyFromConfig.length) return weeklyFromConfig;
|
||||
return canonical.weekly;
|
||||
}
|
||||
|
||||
function todayDateStr() {
|
||||
@@ -182,6 +209,7 @@ function filterActiveLocks(locks) {
|
||||
async function queryLocks(relays, cadence, dateStr, namespace) {
|
||||
const pool = new SimplePool();
|
||||
const tagFilter = `${namespace}-lock-${cadence}-${dateStr}`;
|
||||
const queryTimeoutMs = getQueryTimeoutMs();
|
||||
|
||||
try {
|
||||
const events = await Promise.race([
|
||||
@@ -190,7 +218,7 @@ async function queryLocks(relays, cadence, dateStr, namespace) {
|
||||
'#t': [tagFilter],
|
||||
}),
|
||||
new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('Relay query timed out')), QUERY_TIMEOUT_MS),
|
||||
setTimeout(() => reject(new Error('Relay query timed out')), queryTimeoutMs),
|
||||
),
|
||||
]);
|
||||
|
||||
@@ -464,8 +492,10 @@ Environment:
|
||||
NOSTR_LOCK_NAMESPACE Namespace prefix for lock tags (default: torch)
|
||||
NOSTR_LOCK_RELAYS Comma-separated relay WSS URLs
|
||||
NOSTR_LOCK_TTL Lock TTL in seconds (default: 7200)
|
||||
NOSTR_LOCK_QUERY_TIMEOUT_MS Relay query timeout in milliseconds (default: 15000)
|
||||
NOSTR_LOCK_DAILY_ROSTER Comma-separated daily roster (optional)
|
||||
NOSTR_LOCK_WEEKLY_ROSTER Comma-separated weekly roster (optional)
|
||||
TORCH_CONFIG_PATH Optional path to torch-config.json (default: ./torch-config.json)
|
||||
AGENT_PLATFORM Platform identifier (e.g., codex)
|
||||
|
||||
Exit codes:
|
||||
|
||||
@@ -33,7 +33,10 @@ MUST 3: Run these commands in this order:
|
||||
- Derive `previous_agent` from that file with precedence:
|
||||
a) YAML frontmatter key `agent`.
|
||||
b) Filename format `<timestamp>__<agent-name>__<status>.md`.
|
||||
- If no valid previous log exists (missing file, parse failure, or agent not in roster), set `start_index = 0`.
|
||||
- If no valid previous log exists (missing file, parse failure, or agent not in roster):
|
||||
- Read `scheduler.firstPromptByCadence.daily` from `torch-config.json` if present.
|
||||
- If that agent is in roster, start there.
|
||||
- Otherwise set `start_index = 0`.
|
||||
- Else set `start_index = (index(previous_agent)+1) mod roster_length`.
|
||||
- Round-robin from `start_index`, skipping excluded agents and wrapping with modulo until one eligible agent is found.
|
||||
- If none are eligible, write `_failed.md` with reason `All roster tasks currently claimed by other agents` and stop.
|
||||
@@ -81,7 +84,10 @@ MUST 3: Run these commands in this order:
|
||||
- Derive `previous_agent` from that file with precedence:
|
||||
a) YAML frontmatter key `agent`.
|
||||
b) Filename format `<timestamp>__<agent-name>__<status>.md`.
|
||||
- If no valid previous log exists (missing file, parse failure, or agent not in roster), set `start_index = 0`.
|
||||
- If no valid previous log exists (missing file, parse failure, or agent not in roster):
|
||||
- Read `scheduler.firstPromptByCadence.weekly` from `torch-config.json` if present.
|
||||
- If that agent is in roster, start there.
|
||||
- Otherwise set `start_index = 0`.
|
||||
- Else set `start_index = (index(previous_agent)+1) mod roster_length`.
|
||||
- Round-robin from `start_index`, skipping excluded agents and wrapping with modulo until one eligible agent is found.
|
||||
- If none are eligible, write `_failed.md` with reason `All roster tasks currently claimed by other agents` and stop.
|
||||
|
||||
@@ -43,7 +43,10 @@ Use this document for all scheduler runs.
|
||||
1. Parse YAML frontmatter from `<log_dir>/<latest_file>` and use key `agent` when present and non-empty.
|
||||
2. Otherwise parse filename convention `<timestamp>__<agent-name>__<status>.md` and take `<agent-name>`.
|
||||
- If no valid `latest_file` exists, or parsing fails, or `previous_agent` is not in `roster`, treat as first run fallback.
|
||||
- First run fallback: `start_index = 0`.
|
||||
- First run fallback:
|
||||
- Read `scheduler.firstPromptByCadence.<cadence>` from `torch-config.json` if present.
|
||||
- If that agent exists in `roster`, set `start_index = index(configured_agent)`.
|
||||
- Otherwise set `start_index = 0`.
|
||||
- Otherwise: `start_index = (index(previous_agent in roster) + 1) mod len(roster)`.
|
||||
- Round-robin scan:
|
||||
- Iterate offsets `0..len(roster)-1`.
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
const DEFAULT_CONFIG_PATH = 'torch-config.json';
|
||||
|
||||
let cachedConfig = null;
|
||||
|
||||
function normalizeCadence(value, fallback) {
|
||||
const normalized = String(value || '').trim().toLowerCase();
|
||||
return normalized === 'daily' || normalized === 'weekly' || normalized === 'all' ? normalized : fallback;
|
||||
}
|
||||
|
||||
function normalizeStatus(value, fallback) {
|
||||
const normalized = String(value || '').trim().toLowerCase();
|
||||
return normalized === 'active' || normalized === 'all' ? normalized : fallback;
|
||||
}
|
||||
|
||||
function parseRoster(value) {
|
||||
if (!Array.isArray(value)) return null;
|
||||
const parsed = value.map((item) => String(item).trim()).filter(Boolean);
|
||||
return parsed.length > 0 ? parsed : null;
|
||||
}
|
||||
|
||||
export function getTorchConfigPath() {
|
||||
const explicitPath = (process.env.TORCH_CONFIG_PATH || '').trim();
|
||||
if (explicitPath) return path.resolve(process.cwd(), explicitPath);
|
||||
return path.resolve(process.cwd(), DEFAULT_CONFIG_PATH);
|
||||
}
|
||||
|
||||
export function loadTorchConfig() {
|
||||
if (cachedConfig) return cachedConfig;
|
||||
|
||||
const configPath = getTorchConfigPath();
|
||||
let raw = {};
|
||||
|
||||
if (fs.existsSync(configPath)) {
|
||||
try {
|
||||
raw = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
||||
} catch (err) {
|
||||
throw new Error(`Failed to parse ${configPath}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
const nostrLock = raw.nostrLock || {};
|
||||
const dashboard = raw.dashboard || {};
|
||||
const scheduler = raw.scheduler || {};
|
||||
const firstPromptByCadence = scheduler.firstPromptByCadence || {};
|
||||
|
||||
cachedConfig = {
|
||||
configPath,
|
||||
raw,
|
||||
nostrLock: {
|
||||
namespace: typeof nostrLock.namespace === 'string' ? nostrLock.namespace.trim() : null,
|
||||
relays: Array.isArray(nostrLock.relays)
|
||||
? nostrLock.relays.map((relay) => String(relay).trim()).filter(Boolean)
|
||||
: null,
|
||||
ttlSeconds: Number.isFinite(nostrLock.ttlSeconds) && nostrLock.ttlSeconds > 0
|
||||
? Math.floor(nostrLock.ttlSeconds)
|
||||
: null,
|
||||
queryTimeoutMs: Number.isFinite(nostrLock.queryTimeoutMs) && nostrLock.queryTimeoutMs > 0
|
||||
? Math.floor(nostrLock.queryTimeoutMs)
|
||||
: null,
|
||||
dailyRoster: parseRoster(nostrLock.dailyRoster),
|
||||
weeklyRoster: parseRoster(nostrLock.weeklyRoster),
|
||||
},
|
||||
dashboard: {
|
||||
defaultCadenceView: normalizeCadence(dashboard.defaultCadenceView, 'daily'),
|
||||
defaultStatusView: normalizeStatus(dashboard.defaultStatusView, 'active'),
|
||||
relays: Array.isArray(dashboard.relays)
|
||||
? dashboard.relays.map((relay) => String(relay).trim()).filter(Boolean)
|
||||
: null,
|
||||
namespace: typeof dashboard.namespace === 'string' ? dashboard.namespace.trim() : null,
|
||||
hashtag: typeof dashboard.hashtag === 'string' ? dashboard.hashtag.trim() : null,
|
||||
},
|
||||
scheduler: {
|
||||
firstPromptByCadence: {
|
||||
daily: typeof firstPromptByCadence.daily === 'string' ? firstPromptByCadence.daily.trim() : null,
|
||||
weekly: typeof firstPromptByCadence.weekly === 'string' ? firstPromptByCadence.weekly.trim() : null,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return cachedConfig;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"nostrLock": {
|
||||
"namespace": "torch",
|
||||
"relays": [
|
||||
"wss://relay.damus.io",
|
||||
"wss://nos.lol",
|
||||
"wss://relay.primal.net"
|
||||
],
|
||||
"ttlSeconds": 7200,
|
||||
"queryTimeoutMs": 15000
|
||||
},
|
||||
"dashboard": {
|
||||
"defaultCadenceView": "daily",
|
||||
"defaultStatusView": "active"
|
||||
},
|
||||
"scheduler": {
|
||||
"firstPromptByCadence": {
|
||||
"daily": "scheduler-update-agent",
|
||||
"weekly": "bug-reproducer-agent"
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user