Add AI travel day summaries via OpenRouter for skippers.
Skipper-only proxy with per-entry rate limiting, encrypted payload storage, CSV export, and Plausible tracking. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import { hashEntryForSigning } from './entryCanonicalHash.js'
|
||||
|
||||
describe('hashEntryForSigning', () => {
|
||||
it('excludes aiSummary fields from the signing hash', async () => {
|
||||
const base = {
|
||||
date: '2026-06-03',
|
||||
dayOfTravel: '1',
|
||||
departure: 'A',
|
||||
destination: 'B',
|
||||
freshwater: { morning: 0, refilled: 0, evening: 0, consumption: 0 },
|
||||
fuel: { morning: 0, refilled: 0, evening: 0, consumption: 0 },
|
||||
events: []
|
||||
}
|
||||
|
||||
const withoutSummary = await hashEntryForSigning(base)
|
||||
const withSummary = await hashEntryForSigning({
|
||||
...base,
|
||||
aiSummary: 'A calm day at sea.',
|
||||
aiSummaryGeneratedAt: '2026-06-03T12:00:00.000Z'
|
||||
})
|
||||
|
||||
expect(withSummary).toBe(withoutSummary)
|
||||
})
|
||||
})
|
||||
@@ -1,4 +1,5 @@
|
||||
const SIGNATURE_KEYS = new Set(['signSkipper', 'signCrew'])
|
||||
const AI_SUMMARY_KEYS = new Set(['aiSummary', 'aiSummaryGeneratedAt'])
|
||||
|
||||
function sortEventsByTime(items: unknown[]): unknown[] {
|
||||
return [...items]
|
||||
@@ -25,7 +26,7 @@ function sortValue(value: unknown, parentKey?: string): unknown {
|
||||
const obj = value as Record<string, unknown>
|
||||
const sorted: Record<string, unknown> = {}
|
||||
for (const key of Object.keys(obj).sort()) {
|
||||
if (SIGNATURE_KEYS.has(key)) continue
|
||||
if (SIGNATURE_KEYS.has(key) || AI_SUMMARY_KEYS.has(key)) continue
|
||||
sorted[key] = sortValue(obj[key], key)
|
||||
}
|
||||
return sorted
|
||||
|
||||
Reference in New Issue
Block a user