Files
gstack/design/src/session.ts
T
Garry Tanandcapy-ai[bot] 71acd83aa6 v1.92.11.0 feat: design mockups with Gemini when there's no OpenAI key (#990) (#3768)
* design: make images and read screenshots with Gemini when there is no OpenAI key (#990)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* design: test the live Gemini check; set fixture env keys without secret-shaped assignments

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix: pre-landing review fixes (setup keeps piped keys and names the provider export; Gemini iterate fallback tests)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* design: setup doc comment matches --key-stdin behavior

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix: Gemini saves the final image of a thinking answer; iterate uploads only absolute-path PNGs under 15 MB; sessions store absolute output paths

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* changelog: fragment holds the entry text only

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* todos: Gemini design provider follow-ups

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* docs: the egress fail-open list names design Gemini calls

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-11-01 05:17:06 +00:00

86 lines
2.1 KiB
TypeScript

/**
* Session state management for multi-turn design iteration.
* Session files are JSON in the OS temp directory, keyed by PID + timestamp.
*/
import fs from "fs";
import os from "os";
import path from "path";
import type { ProviderName } from "./auth";
export interface DesignSession {
id: string;
/** The provider that made the images; absent in sessions written before Gemini support, meaning openai. */
provider?: ProviderName;
lastResponseId: string;
originalBrief: string;
feedbackHistory: string[];
outputPaths: string[];
createdAt: string;
updatedAt: string;
}
/**
* Generate a unique session ID from PID + timestamp.
*/
export function createSessionId(): string {
return `${process.pid}-${Date.now()}`;
}
/**
* Get the file path for a session.
*/
export function sessionPath(sessionId: string): string {
return path.join(os.tmpdir(), `design-session-${sessionId}.json`);
}
/**
* Create a new session after initial generation.
*/
export function createSession(
responseId: string,
brief: string,
outputPath: string,
provider: ProviderName = "openai",
): DesignSession {
const id = createSessionId();
const session: DesignSession = {
id,
provider,
lastResponseId: responseId,
originalBrief: brief,
feedbackHistory: [],
outputPaths: [path.resolve(outputPath)],
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
};
fs.writeFileSync(sessionPath(id), JSON.stringify(session, null, 2), { mode: 0o600 });
return session;
}
/**
* Read an existing session from disk.
*/
export function readSession(sessionFilePath: string): DesignSession {
const content = fs.readFileSync(sessionFilePath, "utf-8");
return JSON.parse(content);
}
/**
* Update a session with new iteration data.
*/
export function updateSession(
session: DesignSession,
responseId: string,
feedback: string,
outputPath: string,
): void {
session.lastResponseId = responseId;
session.feedbackHistory.push(feedback);
session.outputPaths.push(path.resolve(outputPath));
session.updatedAt = new Date().toISOString();
fs.writeFileSync(sessionPath(session.id), JSON.stringify(session, null, 2));
}