From 2466c80af9f985eb88ce00205a4e4bb5d82a7d8f Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Tue, 17 Feb 2026 19:52:10 +0000 Subject: [PATCH] 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 <53631862+PR0M3TH3AN@users.noreply.github.com> --- .scheduler-memory/latest/daily/memories.md | 2 +- .scheduler-memory/memory-store.json | 52 +++++++++++ .../session-mlr0921p/retrieve.json | 18 ++++ .../session-mlr0921p/retrieve.ok | 1 + .scheduler-memory/session-mlr0g2tv/store.json | 20 +++++ .scheduler-memory/session-mlr0g2tv/store.ok | 1 + dashboard.log | 8 ++ dashboard_verify.log | 3 +- package-lock.json | 64 +++++++++++++ package.json | 1 + scripts/agent/debug_frontend.mjs | 49 ++++++++++ src/cmd-check.mjs | 2 +- src/dashboard.mjs | 1 + src/lock-ops.mjs | 89 ++++++++++++++----- ...frontend-console-debug-agent__completed.md | 34 +++++++ 15 files changed, 321 insertions(+), 24 deletions(-) create mode 100644 .scheduler-memory/memory-store.json create mode 100644 .scheduler-memory/session-mlr0921p/retrieve.json create mode 100644 .scheduler-memory/session-mlr0921p/retrieve.ok create mode 100644 .scheduler-memory/session-mlr0g2tv/store.json create mode 100644 .scheduler-memory/session-mlr0g2tv/store.ok create mode 100644 dashboard.log create mode 100644 scripts/agent/debug_frontend.mjs create mode 100644 task-logs/weekly/2026-02-17T19-44-07Z__frontend-console-debug-agent__completed.md diff --git a/.scheduler-memory/latest/daily/memories.md b/.scheduler-memory/latest/daily/memories.md index 55bdfd5..b6762ce 100644 --- a/.scheduler-memory/latest/daily/memories.md +++ b/.scheduler-memory/latest/daily/memories.md @@ -1 +1 @@ -- Memory retrieval seed for daily :: Required startup + artifacts + memory + issue capture \ No newline at end of file +- Memory retrieval seed for daily :: scheduler memory retrieval \ No newline at end of file diff --git a/.scheduler-memory/memory-store.json b/.scheduler-memory/memory-store.json new file mode 100644 index 0000000..0d62bb3 --- /dev/null +++ b/.scheduler-memory/memory-store.json @@ -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 + } + ] +] \ No newline at end of file diff --git a/.scheduler-memory/session-mlr0921p/retrieve.json b/.scheduler-memory/session-mlr0921p/retrieve.json new file mode 100644 index 0000000..30c5bcf --- /dev/null +++ b/.scheduler-memory/session-mlr0921p/retrieve.json @@ -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" +} \ No newline at end of file diff --git a/.scheduler-memory/session-mlr0921p/retrieve.ok b/.scheduler-memory/session-mlr0921p/retrieve.ok new file mode 100644 index 0000000..84b1719 --- /dev/null +++ b/.scheduler-memory/session-mlr0921p/retrieve.ok @@ -0,0 +1 @@ +MEMORY_RETRIEVED diff --git a/.scheduler-memory/session-mlr0g2tv/store.json b/.scheduler-memory/session-mlr0g2tv/store.json new file mode 100644 index 0000000..f419ee1 --- /dev/null +++ b/.scheduler-memory/session-mlr0g2tv/store.json @@ -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" +} \ No newline at end of file diff --git a/.scheduler-memory/session-mlr0g2tv/store.ok b/.scheduler-memory/session-mlr0g2tv/store.ok new file mode 100644 index 0000000..1e40bb6 --- /dev/null +++ b/.scheduler-memory/session-mlr0g2tv/store.ok @@ -0,0 +1 @@ +MEMORY_STORED diff --git a/dashboard.log b/dashboard.log new file mode 100644 index 0000000..b9f5847 --- /dev/null +++ b/dashboard.log @@ -0,0 +1,8 @@ + +> torch-lock@0.1.0 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 diff --git a/dashboard_verify.log b/dashboard_verify.log index 739f71f..b9f5847 100644 --- a/dashboard_verify.log +++ b/dashboard_verify.log @@ -2,6 +2,7 @@ > torch-lock@0.1.0 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 diff --git a/package-lock.json b/package-lock.json index 539e8cc..a4947d7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index ab950a3..d9dc7d9 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ }, "devDependencies": { "@eslint/js": "^10.0.1", + "@playwright/test": "^1.58.2", "eslint": "^10.0.0", "globals": "^17.3.0" } diff --git a/scripts/agent/debug_frontend.mjs b/scripts/agent/debug_frontend.mjs new file mode 100644 index 0000000..78a9688 --- /dev/null +++ b/scripts/agent/debug_frontend.mjs @@ -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); + } +})(); diff --git a/src/cmd-check.mjs b/src/cmd-check.mjs index 2ca8fe3..8d81a27 100644 --- a/src/cmd-check.mjs +++ b/src/cmd-check.mjs @@ -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); diff --git a/src/dashboard.mjs b/src/dashboard.mjs index bf9eea2..740357a 100644 --- a/src/dashboard.mjs +++ b/src/dashboard.mjs @@ -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') ]; diff --git a/src/lock-ops.mjs b/src/lock-ops.mjs index 50dbb9d..582e7f7 100644 --- a/src/lock-ops.mjs +++ b/src/lock-ops.mjs @@ -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` diff --git a/task-logs/weekly/2026-02-17T19-44-07Z__frontend-console-debug-agent__completed.md b/task-logs/weekly/2026-02-17T19-44-07Z__frontend-console-debug-agent__completed.md new file mode 100644 index 0000000..ea4dcbe --- /dev/null +++ b/task-logs/weekly/2026-02-17T19-44-07Z__frontend-console-debug-agent__completed.md @@ -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`)