import * as plugins from './plugins.js'; import { ControllerCodexProfileModel, ControllerCodexMappingModel, assertCodexProfileDocument, codexMappingDocumentId, validateCodexServerUrl, type IControllerCodexProfileDocument, type IControllerCodexMappingDocument, } from './classes.codexconnectionmodels.js'; import { isCodexCreationToken } from './functions.codexidentity.js'; import type { IControllerCodexProfile } from '../ts_interfaces/index.js'; const updateId = () => plugins.crypto.randomBytes(32).toString('base64url'); const ambiguous = (errorArg: unknown): boolean => errorArg instanceof plugins.smartdata.SmartdataExactPersistenceError && errorArg.code === 'ambiguous_write'; /** Profile endpoints are immutable. Only names, encrypted credentials and retirement may change. */ export class ControllerCodexConnectionStore { private mutationTail: Promise = Promise.resolve(); private closed = false; constructor(public readonly issuerId: string) { if (!isCodexCreationToken(issuerId, 32)) throw new Error('Invalid Codex profile issuer.'); } public stableId(kindArg: string): string { return plugins.crypto.createHash('sha256').update(JSON.stringify(['codex-profile-v1', this.issuerId, kindArg])).digest('base64url'); } public publicProfile(profileArg: IControllerCodexProfileDocument): IControllerCodexProfile { assertCodexProfileDocument(profileArg); return { id: profileArg.id, name: profileArg.profileName, mode: profileArg.mode, ...(profileArg.serverUrl ? { serverUrl: profileArg.serverUrl } : {}), hasCredential: profileArg.credentialCiphertext !== undefined, credentialRevision: profileArg.credentialRevision, state: profileArg.state }; } public async list(): Promise { const stored = await ControllerCodexProfileModel.exact.findStored({ filter: { issuerId: this.issuerId }, sort: { id: 1 }, limit: 65 }); if (stored.length > 64) throw new Error('Codex profile capacity exceeded.'); return stored.map(entry => structuredClone(ControllerCodexProfileModel.exact.toPersisted(entry))); } public async get(idArg: string): Promise { if (!isCodexCreationToken(idArg, 32)) throw new Error('Invalid Codex profile identity.'); const entry = await ControllerCodexProfileModel.exact.findStoredOne({ id: idArg, issuerId: this.issuerId }); if (!entry) throw new Error('Codex connection profile was not found.'); return structuredClone(ControllerCodexProfileModel.exact.toPersisted(entry)); } public create(inputArg: { id?: string; name: string; serverUrl?: string; token?: string }): Promise { return this.mutate(async () => { if ((await this.list()).length >= 64) throw new Error('Codex profile capacity reached.'); const id = inputArg.id ?? updateId(); const now = new Date(); const candidate: IControllerCodexProfileDocument = { id, issuerId: this.issuerId, profileName: inputArg.name, mode: inputArg.serverUrl ? 'remote' : 'local', state: 'active', createdAt: now, updatedAt: now, updateId: updateId(), credentialRevision: inputArg.token === undefined ? 0 : 1, ...(inputArg.serverUrl ? { serverUrl: validateCodexServerUrl(inputArg.serverUrl) } : {}), ...(inputArg.token === undefined ? {} : { credentialCiphertext: await this.seal(id, inputArg.token) }) }; assertCodexProfileDocument(candidate); try { const inserted = await ControllerCodexProfileModel.exact.insert(candidate); const body = ControllerCodexProfileModel.exact.toPersisted(inserted.document); if (body.updateId !== candidate.updateId) throw new Error('Codex profile already exists.'); return structuredClone(body); } catch (error) { if (!ambiguous(error)) throw error; const body = await this.get(id); if (body.updateId !== candidate.updateId) throw new Error('Codex profile creation has an uncertain persistence outcome.'); return body; } }); } public update(idArg: string, inputArg: { name?: string; token?: string | null; retire?: true; expectedCredentialRevision: number }): Promise { return this.mutate(async () => { const stored = await ControllerCodexProfileModel.exact.findStoredOne({ id: idArg, issuerId: this.issuerId }); if (!stored) throw new Error('Codex connection profile was not found.'); const current = ControllerCodexProfileModel.exact.toPersisted(stored); if (current.credentialRevision !== inputArg.expectedCredentialRevision) throw new Error('Codex credentials changed in another request. Refresh before editing.'); if (current.state !== 'active') throw new Error('A retired Codex profile cannot be edited.'); const candidate = structuredClone(current); if (inputArg.name !== undefined) candidate.profileName = inputArg.name; if (inputArg.retire) { if (current.mode === 'local') throw new Error('The local Codex profile cannot be retired.'); candidate.state = 'retired'; } if (inputArg.token !== undefined) { if (current.mode !== 'remote') throw new Error('Local Codex credentials are managed by codex login.'); if (inputArg.token === null) delete candidate.credentialCiphertext; else candidate.credentialCiphertext = await this.seal(idArg, inputArg.token); candidate.credentialRevision += 1; } candidate.updatedAt = new Date(); candidate.updateId = updateId(); assertCodexProfileDocument(candidate); try { const result = await ControllerCodexProfileModel.exact.transition({ current: stored, change: model => { Object.assign(model, candidate); if (!candidate.credentialCiphertext) delete model.credentialCiphertext; } }); if (result.status !== 'transitioned') throw new Error('Codex profile changed concurrently.'); return structuredClone(ControllerCodexProfileModel.exact.toPersisted(result.document)); } catch (error) { if (!ambiguous(error)) throw error; const body = await this.get(idArg); if (body.updateId !== candidate.updateId) throw new Error('Codex profile update has an uncertain persistence outcome.'); return body; } }); } public async mapping(projectIdArg: string): Promise { const entry = await ControllerCodexMappingModel.exact.findStoredOne({ id: codexMappingDocumentId(this.issuerId, projectIdArg), issuerId: this.issuerId }); return entry ? structuredClone(ControllerCodexMappingModel.exact.toPersisted(entry)) : undefined; } public setMapping(projectIdArg: string, profileIdArg: string, remoteDirectoryArg: string): Promise { return this.mutate(async () => { const profile = await this.get(profileIdArg); if (profile.state !== 'active') throw new Error('New conversations require an active Codex connection profile.'); const id = codexMappingDocumentId(this.issuerId, projectIdArg); const current = await ControllerCodexMappingModel.exact.findStoredOne({ id, issuerId: this.issuerId }); const now = new Date(); const candidate: IControllerCodexMappingDocument = { id, issuerId: this.issuerId, projectId: projectIdArg, profileId: profileIdArg, remoteDirectory: remoteDirectoryArg, createdAt: current ? ControllerCodexMappingModel.exact.toPersisted(current).createdAt : now, updatedAt: now, updateId: updateId() }; try { if (!current) { const inserted = await ControllerCodexMappingModel.exact.insert(candidate); if (ControllerCodexMappingModel.exact.toPersisted(inserted.document).updateId !== candidate.updateId) throw new Error('Codex mapping changed concurrently.'); } else { const result = await ControllerCodexMappingModel.exact.transition({ current, change: model => { Object.assign(model, candidate); } }); if (result.status !== 'transitioned') throw new Error('Codex mapping changed concurrently.'); } } catch (error) { if (!ambiguous(error)) throw error; const found = await this.mapping(projectIdArg); if (found?.updateId !== candidate.updateId) throw new Error('Codex mapping update has an uncertain persistence outcome.'); } return candidate; }); } public async token(profileArg: IControllerCodexProfileDocument): Promise { if (!profileArg.credentialCiphertext) return undefined; const ciphertext = Buffer.from(profileArg.credentialCiphertext, 'base64url'); let plaintext: Uint8Array | undefined; try { plaintext = await plugins.smartsecret.unsealSmartSecretTpm2Credential(this.credentialName(profileArg.id), ciphertext); const token = Buffer.from(plaintext.buffer, plaintext.byteOffset, plaintext.byteLength).toString('utf8'); this.assertToken(token); return token; } finally { ciphertext.fill(0); plaintext?.fill(0); } } private async seal(idArg: string, tokenArg: string): Promise { this.assertToken(tokenArg); const bytes = Buffer.from(tokenArg, 'utf8'); let sealed: Uint8Array | undefined; try { sealed = await plugins.smartsecret.sealSmartSecretTpm2Credential(this.credentialName(idArg), bytes); return Buffer.from(sealed.buffer, sealed.byteOffset, sealed.byteLength).toString('base64url'); } finally { bytes.fill(0); sealed?.fill(0); } } private assertToken(tokenArg: string): void { if (typeof tokenArg !== 'string' || !/^[\x21-\x7e]{1,8192}$/.test(tokenArg)) throw new Error('Codex tokens must contain 1–8192 printable ASCII characters without spaces.'); } private credentialName(idArg: string): string { return `agl-codex-${this.issuerId}-${idArg}`; } private mutate(operationArg: () => Promise): Promise { if (this.closed) return Promise.reject(new Error('Codex profile persistence is closed.')); const task = this.mutationTail.then(operationArg); this.mutationTail = task.then(() => undefined, () => undefined); return task; } public async close(): Promise { this.closed = true; await this.mutationTail; } }