fix(dashboard): Allow access to src/constants.mjs and fix lock-ops

`frontend-console-debug-agent` run:
- Fixed a 403 Forbidden error in `src/dashboard.mjs` preventing `src/constants.mjs` from loading by adding it to the `allowedPaths` whitelist.
- Refactored `src/lock-ops.mjs` to fix a critical syntax error in `LockPublisher` class.
- Fixed a typo in `src/cmd-check.mjs` (`queryLocksFn` -> `queryLocks`).
- Added `@playwright/test` for frontend debugging.
- Added `scripts/agent/debug_frontend.mjs` for reproduction.

Co-authored-by: PR0M3TH3AN <[email protected]>
This commit is contained in:
google-labs-jules[bot]andPR0M3TH3AN committed 2026-02-17 19:52:10 +00:00
1 parent 7f87c371fc
commit 2466c80af9
15 files changed
+321 -24

No files matched your search

+1 -1
View File
@@ -1 +1 @@
- Memory retrieval seed for daily :: Required startup + artifacts + memory + issue capture
- Memory retrieval seed for daily :: scheduler memory retrieval
+52
View File
@@ -0,0 +1,52 @@
[
[
"4ef68b6d-5d99-4b32-a275-596f5daee8a0",
{
"schema_version": 1,
"id": "4ef68b6d-5d99-4b32-a275-596f5daee8a0",
"agent_id": "scheduler-memory-daily",
"session_id": "session-mlr0921p",
"type": "batch_window",
"content": "Memory retrieval seed for daily :: scheduler memory retrieval",
"summary": "Memory retrieval seed for daily :: scheduler memory retrieval",
"tags": [
"scheduler",
"daily",
"retrieve"
],
"importance": 0.4,
"embedding_id": null,
"created_at": 1771357048477,
"last_seen": 1771357048488,
"source": "scheduler-retrieve",
"ttl_days": null,
"merged_into": null,
"pinned": false
}
],
[
"68f82e5f-11a4-4ea8-9e86-349ee14b62d4",
{
"schema_version": 1,
"id": "68f82e5f-11a4-4ea8-9e86-349ee14b62d4",
"agent_id": "scheduler-memory-weekly",
"session_id": "session-mlr0g2tv",
"type": "batch_window",
"content": "Store memory event A for weekly :: scheduler memory store\nStore memory event B for weekly :: scheduler memory store",
"summary": "Store memory event A for weekly :: scheduler memory store Store memory event B for weekly :: scheduler memory store",
"tags": [
"scheduler",
"weekly",
"store"
],
"importance": 0.55,
"embedding_id": null,
"created_at": 1771357376083,
"last_seen": 1771357376084,
"source": "scheduler-store",
"ttl_days": null,
"merged_into": null,
"pinned": false
}
]
]
@@ -0,0 +1,18 @@
{
"cadence": "daily",
"operation": "retrieve",
"runId": "session-mlr0921p",
"servicePath": "src/services/memory/index.js#getRelevantMemories",
"inputs": {
"agentId": "scheduler-memory-daily",
"promptPath": "",
"promptIntent": "scheduler memory retrieval",
"query": "scheduler | daily | agent | scheduler-memory-daily | intent | scheduler memory retrieval",
"events": 1
},
"outputs": {
"ingestedCount": 1,
"retrievedCount": 1
},
"status": "ok"
}
@@ -0,0 +1 @@
MEMORY_RETRIEVED
@@ -0,0 +1,20 @@
{
"cadence": "weekly",
"operation": "store",
"runId": "session-mlr0g2tv",
"servicePath": "src/services/memory/index.js#ingestEvents",
"inputs": {
"agentId": "scheduler-memory-weekly",
"promptPath": "",
"promptIntent": "scheduler memory store",
"events": 2,
"sourceFile": "fallback"
},
"outputs": {
"storedCount": 1,
"summaries": [
"Store memory event A for weekly :: scheduler memory store Store memory event B for weekly :: scheduler memory store"
]
},
"status": "ok"
}
@@ -0,0 +1 @@
MEMORY_STORED
+8
View File
@@ -0,0 +1,8 @@
> [email protected] dashboard:serve
> node bin/torch-lock.mjs dashboard
Dashboard running at http://null:4173/dashboard/
Authentication: DISABLED (Dashboard is public)
Serving files from /app
Using configuration from /app
+2 -1
View File
@@ -2,6 +2,7 @@
> [email protected] dashboard:serve
> node bin/torch-lock.mjs dashboard
Dashboard running at http://localhost:4173/dashboard/
Dashboard running at http://null:4173/dashboard/
Authentication: DISABLED (Dashboard is public)
Serving files from /app
Using configuration from /app
+64
View File
@@ -16,6 +16,7 @@
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@playwright/test": "^1.58.2",
"eslint": "^10.0.0",
"globals": "^17.3.0"
}
@@ -255,6 +256,22 @@
"url": "https://paulmillr.com/funding/"
}
},
"node_modules/@playwright/test": {
"version": "1.58.2",
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.58.2.tgz",
"integrity": "sha512-akea+6bHYBBfA9uQqSYmlJXn61cTa+jbO87xVLCWbTqbWadRVmhxlXATaOjOgcBaWU4ePo0wB41KMFv3o35IXA==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright": "1.58.2"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/@scure/base": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.1.tgz",
@@ -670,6 +687,21 @@
"dev": true,
"license": "ISC"
},
"node_modules/fsevents": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
"node_modules/glob-parent": {
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@@ -952,6 +984,38 @@
"node": ">=8"
}
},
"node_modules/playwright": {
"version": "1.58.2",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.58.2.tgz",
"integrity": "sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.58.2"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=18"
},
"optionalDependencies": {
"fsevents": "2.3.2"
}
},
"node_modules/playwright-core": {
"version": "1.58.2",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz",
"integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=18"
}
},
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+1
View File
@@ -56,6 +56,7 @@
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@playwright/test": "^1.58.2",
"eslint": "^10.0.0",
"globals": "^17.3.0"
}
+49
View File
@@ -0,0 +1,49 @@
import { chromium } from '@playwright/test';
(async () => {
const browser = await chromium.launch();
const page = await browser.newPage();
let errorCount = 0;
page.on('console', msg => {
const type = msg.type();
if (type === 'error' || type === 'warning') {
console.log(`CONSOLE [${type.toUpperCase()}]: ${msg.text()}`);
if (type === 'error') errorCount++;
}
});
page.on('pageerror', exception => {
console.log(`PAGE ERROR: ${exception}`);
errorCount++;
});
page.on('requestfailed', request => {
// Check if failure() is not null before accessing errorText
const failure = request.failure();
const errorText = failure ? failure.errorText : 'Unknown error';
console.log(`REQUEST FAILED: ${request.url()} - ${errorText}`);
errorCount++;
});
try {
console.log('Navigating to http://localhost:4173/dashboard/ ...');
await page.goto('http://localhost:4173/dashboard/');
console.log('Page loaded. Waiting 5s for any async errors...');
await page.waitForTimeout(5000);
} catch (e) {
console.error('Navigation failed:', e);
errorCount++;
}
await browser.close();
if (errorCount > 0) {
console.log(`\nFound ${errorCount} errors.`);
process.exit(1);
} else {
console.log('\nNo errors found.');
process.exit(0);
}
})();
+1 -1
View File
@@ -20,7 +20,7 @@ export async function cmdCheck(cadence) {
console.error(`Paused agents: ${pausedAgents.join(', ')}`);
}
const locks = await queryLocksFn(relays, cadence, dateStr, namespace);
const locks = await queryLocks(relays, cadence, dateStr, namespace);
const lockedAgents = [...new Set(locks.map((l) => l.agent).filter(Boolean))];
const roster = await getRoster(cadence);
+1
View File
@@ -136,6 +136,7 @@ export async function cmdDashboard(port = DEFAULT_DASHBOARD_PORT, host = '127.0.
path.join(packageRoot, 'dashboard'),
path.join(packageRoot, 'assets'),
path.join(packageRoot, 'src', 'docs'),
path.join(packageRoot, 'src', 'constants.mjs'),
path.join(packageRoot, 'torch-config.json')
];
+68 -21
View File
@@ -1,4 +1,4 @@
import { randomInt } from 'node:crypto';
import { randomInt, randomUUID } from 'node:crypto';
import { SimplePool } from 'nostr-tools/pool';
import {
getQueryTimeoutMs,
@@ -415,26 +415,73 @@ async function publishToRelays(pool, relays, event, publishTimeoutMs, phase) {
});
}
export async function publishLock(relays, event, deps = {}) {
const {
poolFactory = () => new SimplePool(),
getPublishTimeoutMsFn = getPublishTimeoutMs,
getMinSuccessfulRelayPublishesFn = getMinSuccessfulRelayPublishes,
getRelayFallbacksFn = getRelayFallbacks,
getMinActiveRelayPoolFn = getMinActiveRelayPool,
retryAttempts = 4,
retryBaseDelayMs = 500,
retryCapDelayMs = 8_000,
sleepFn = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
randomFn = secureRandom,
telemetryLogger = console.error,
healthLogger = console.error,
diagnostics = {},
} = deps;
export class LockPublisher {
constructor(relays, event, deps = {}) {
this.relays = relays;
this.event = event;
this.deps = deps;
this.pool = null;
this.healthConfig = null;
this.allRelays = [];
this.fallbackRelays = [];
this.publishTimeoutMs = 0;
this.minSuccesses = 0;
this.minActiveRelayPool = 0;
this.maxAttempts = 0;
this.retryBaseDelayMs = 0;
this.retryCapDelayMs = 0;
this.sleepFn = null;
this.randomFn = null;
this.telemetryLogger = null;
this.healthLogger = null;
this.healthManager = null;
this.correlationId = null;
this.attemptId = null;
}
async publish() {
const {
poolFactory = () => new SimplePool(),
getPublishTimeoutMsFn = getPublishTimeoutMs,
getMinSuccessfulRelayPublishesFn = getMinSuccessfulRelayPublishes,
getRelayFallbacksFn = getRelayFallbacks,
getMinActiveRelayPoolFn = getMinActiveRelayPool,
retryAttempts = 4,
retryBaseDelayMs = 500,
retryCapDelayMs = 8_000,
sleepFn = (ms) => new Promise((resolve) => setTimeout(resolve, ms)),
randomFn = secureRandom,
telemetryLogger = console.error,
healthLogger = console.error,
healthManager = defaultHealthManager,
diagnostics = {},
} = this.deps;
this.pool = poolFactory();
this.publishTimeoutMs = await getPublishTimeoutMsFn();
this.minSuccesses = await getMinSuccessfulRelayPublishesFn();
this.fallbackRelays = await getRelayFallbacksFn();
this.minActiveRelayPool = await getMinActiveRelayPoolFn();
this.maxAttempts = retryAttempts;
this.retryBaseDelayMs = retryBaseDelayMs;
this.retryCapDelayMs = retryCapDelayMs;
this.sleepFn = sleepFn;
this.randomFn = randomFn;
this.telemetryLogger = telemetryLogger;
this.healthLogger = healthLogger;
this.healthManager = healthManager;
this.correlationId = diagnostics.correlationId || randomUUID();
this.attemptId = diagnostics.attemptId || randomUUID();
this.healthConfig = buildRelayHealthConfig({
...this.deps,
minActiveRelayPool: this.minActiveRelayPool
});
this.allRelays = mergeRelayList(this.relays, this.fallbackRelays);
try {
maybeLogHealthSnapshot(this.allRelays, this.healthConfig, this.healthLogger, 'publish:periodic');
this.healthManager.maybeLogSnapshot(this.allRelays, this.healthConfig, this.healthLogger, 'publish:periodic');
let lastAttemptState = null;
const retryTimeline = [];
const overallStartedAt = Date.now();
@@ -500,7 +547,7 @@ export async function publishLock(relays, event, deps = {}) {
async attemptPhase(phaseRelays, phaseName, attempted, publishResults) {
if (!phaseRelays.length) return;
const { prioritized } = prioritizeRelays(phaseRelays, this.healthConfig);
const { prioritized } = this.healthManager.prioritizeRelays(phaseRelays, this.healthConfig);
if (!prioritized.length) return;
console.error(`[${phaseName}] Publishing to ${prioritized.length} relays (${prioritized.join(', ')})...`);
@@ -513,7 +560,7 @@ export async function publishLock(relays, event, deps = {}) {
const elapsedMs = Date.now() - startedAtMs;
for (const result of phaseResults) {
result.latencyMs = elapsedMs;
recordRelayOutcome(result.relay, result.success, result.message, elapsedMs, this.healthConfig);
this.healthManager.recordOutcome(result.relay, result.success, result.message, elapsedMs, this.healthConfig);
}
publishResults.push(...phaseResults);
}
@@ -601,7 +648,7 @@ export async function publishLock(relays, event, deps = {}) {
totalElapsedMs: Date.now() - overallStartedAt,
}));
maybeLogHealthSnapshot(this.allRelays, this.healthConfig, this.healthLogger, 'publish:failure', true);
this.healthManager.maybeLogSnapshot(this.allRelays, this.healthConfig, this.healthLogger, 'publish:failure', true);
throw new Error(
`Failed relay publish quorum in publish phase: ${lastAttemptState.successCount}/${lastAttemptState.attempted.size} successful `
+ `(required=${this.minSuccesses}, timeout=${this.publishTimeoutMs}ms, attempts=${this.maxAttempts}, attempt_id=${this.attemptId}, correlation_id=${this.correlationId}, error_category=${terminalFailureCategory}, total_retry_timeline_ms=${Date.now() - overallStartedAt})\n`
@@ -0,0 +1,34 @@
---
agent: frontend-console-debug-agent
status: completed
date: 2026-02-17
---
# Weekly Frontend Console Debug
## Summary
Detected and resolved a `403 Forbidden` error preventing the dashboard from loading `src/constants.mjs`.
## Actions
1. **Fixed Infrastructure**:
- Refactored `src/lock-ops.mjs` to fix a critical syntax error in the `LockPublisher` class.
- Verified the fix by successfully running `npm run lock:check:weekly`.
2. **Frontend Debugging**:
- Installed `@playwright/test` to capture console errors.
- Created `scripts/agent/debug_frontend.mjs` which launches a headless browser to inspect the dashboard.
- Identified a blocking `403 Forbidden` error when the dashboard attempted to load `src/constants.mjs`.
3. **Remediation**:
- Updated `src/dashboard.mjs` to explicitly allow serving `src/constants.mjs` by adding it to the `allowedPaths` whitelist. This allows the frontend to access shared constants while maintaining security restrictions on other source files.
- Fixed a typo in `src/cmd-check.mjs` where `queryLocksFn` was referenced instead of `queryLocks`.
4. **Verification**:
- Re-ran `scripts/agent/debug_frontend.mjs` and confirmed the dashboard loads with zero console errors.
- Ran `npm run lint` (passed with warnings).
- Ran `npm run build` (successful).
- Ran `npm run test:unit:lock-backend` (all tests passed).
## Artifacts
- `scripts/agent/debug_frontend.mjs`
- `package.json` (added `@playwright/test`)