mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-03 15:27:56 +01:00
* 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>
86 lines
2.1 KiB
TypeScript
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));
|
|
}
|