ci/woodpecker/pr/ci Pipeline failed
Add the Slice-Zero catalog and selection HTTP surfaces for P3 Task 3: GET /api/harnesses, GET /api/harnesses/:harnessId/catalog, GET+PUT /api/chat/preferences/selection. Scope is always server-derived via scopeFromUser(CurrentUser); selection tuples are validated against the live catalog with no fallback substitution and persisted in a transitional owner-scoped in-memory store. HarnessModule is wired into AppModule. Co-Authored-By: Claude Opus 4.8 <[email protected]> Claude-Session: https://claude.ai/code/session_01ESFAnh2t9HmLwng8oW95St
40 lines
1.5 KiB
TypeScript
40 lines
1.5 KiB
TypeScript
import { Injectable } from '@nestjs/common';
|
|
import type { HarnessSelection } from '@mosaicstack/types';
|
|
import type { ActorTenantScope } from '../auth/session-scope.js';
|
|
|
|
/**
|
|
* TRANSITIONAL Slice-Zero selection store.
|
|
*
|
|
* This is an EXPLICITLY in-memory, owner-scoped store. It exists only so the
|
|
* Slice-Zero HTTP surfaces round-trip a selection; it is NOT durable and does not
|
|
* survive a process restart. Task Seven replaces it with the durable selection
|
|
* repository backed by the database.
|
|
*
|
|
* The store is keyed STRICTLY by the server-derived scope (userId + tenantId).
|
|
* There is no code path through which a client-supplied id chooses the key.
|
|
*/
|
|
@Injectable()
|
|
export class HarnessSelectionRepository {
|
|
private readonly byOwner = new Map<string, HarnessSelection>();
|
|
|
|
get(scope: ActorTenantScope): HarnessSelection | null {
|
|
return this.byOwner.get(ownerKey(scope)) ?? null;
|
|
}
|
|
|
|
set(scope: ActorTenantScope, selection: HarnessSelection): HarnessSelection {
|
|
// Store a normalized copy of exactly the tuple fields — nothing else is retained.
|
|
const stored: HarnessSelection = {
|
|
harnessId: selection.harnessId,
|
|
providerId: selection.providerId,
|
|
modelId: selection.modelId,
|
|
};
|
|
this.byOwner.set(ownerKey(scope), stored);
|
|
return stored;
|
|
}
|
|
}
|
|
|
|
/** Owner key from the server-derived scope only. NUL-separated to avoid collisions. */
|
|
function ownerKey(scope: ActorTenantScope): string {
|
|
return `${scope.userId} |