import type { IIdeSshTarget } from '@git.zone/ide-protocol'; import * as fs from 'node:fs/promises'; import * as os from 'node:os'; import * as path from 'node:path'; import * as plugins from './plugins.js'; export interface IAgentProjectContext { instanceId: string; title: string; path: string; runtimeRoot: string; target: IIdeSshTarget; batchMode: boolean; } export interface IAgentEventEnvelope { instanceId: string; event: IAgentEvent; } type TAgentStatus = | { type: 'idle' } | { type: 'busy'; message?: string } | { type: 'error'; message: string }; interface IAgentEvent { type: string; data?: Record; } interface IAgentMessage { id: string; role: 'user' | 'assistant' | 'tool'; text: string; createdAt: string; updatedAt?: string; } interface IAgentSessionDescriptor { id: string; title: string; createdAt: string; updatedAt: string; } interface IPendingPermission { id: string; key: string; resolve: () => void; reject: (error: Error) => void; } interface IAgentSession extends IAgentSessionDescriptor { instanceId: string; messages: IAgentMessage[]; status: TAgentStatus; abortController?: AbortController; pendingPermissions: Map; rememberedPermissions: Set; } interface IPersistedAgentSession { id: string; title: string; createdAt: string; updatedAt: string; messages: IAgentMessage[]; rememberedPermissions: string[]; } interface IPersistedAgentSessionsFile { version: 1; project: { title: string; path: string; target: { hostAlias: string; hostName?: string; user?: string; port?: number; }; }; sessions: IPersistedAgentSession[]; } interface IAgentSessionInput { instanceId?: string; sessionId?: string; } interface ICreateAgentSessionInput { instanceId?: string; title?: string; } interface IAgentPromptInput extends IAgentSessionInput { text?: string; } interface IAgentPermissionResponseInput extends IAgentSessionInput { permissionId?: string; response?: 'once' | 'always' | 'reject'; remember?: boolean; } type TEmitAgentEvent = (payload: IAgentEventEnvelope) => void; type TResolveProjectContext = (instanceId: string) => IAgentProjectContext | undefined; interface IOpenAiMaxIdTokenInfo { chatgptAccountId?: string; expiresAt?: string; rawJwt: string; } interface IOpenAiMaxTokenData { accessToken: string; refreshToken: string; idToken: string; idTokenInfo: IOpenAiMaxIdTokenInfo; accountId?: string; } interface ILooseSmartAiModule { getModel: (options: { provider: 'openai'; model: string; apiKey?: string; openAiMaxAuth?: IOpenAiMaxTokenData }) => unknown; parseOpenAiMaxIdToken: (idToken: string) => IOpenAiMaxIdTokenInfo; refreshOpenAiMaxTokenData: (tokenData: IOpenAiMaxTokenData) => Promise; } interface ILooseZodBuilder { default: (value: unknown) => ILooseZodBuilder; describe: (description: string) => ILooseZodBuilder; optional: () => ILooseZodBuilder; } interface ILooseSmartAgentModule { runAgent: (options: { model: unknown; prompt: string; system?: string; messages?: Array<{ role: 'user' | 'assistant'; content: string }>; tools?: Record; sessionId?: string; maxSteps?: number; cache?: 'auto' | false; abort?: AbortSignal; onToken?: (delta: string) => void; onToolCall?: (toolName: string, toolInput: unknown) => void; onToolResult?: (toolName: string, toolResult: unknown) => void; }) => Promise<{ text?: string }>; tool: (options: { description: string; inputSchema: unknown; execute: (input: TInput) => Promise | string; }) => unknown; truncateOutput: (text: string, options?: { maxLines?: number; maxBytes?: number }) => { content: string; truncated: boolean; notice?: string }; z: { object: (shape: Record) => unknown; number: () => ILooseZodBuilder; string: () => ILooseZodBuilder; }; } const smartAgentPackageName: string = '@push.rocks/smartagent'; const smartAiPackageName: string = '@push.rocks/smartai'; let smartAgentModulePromise: Promise | undefined; let smartAiModulePromise: Promise | undefined; const loadSmartAgent = (): Promise => { smartAgentModulePromise ??= import(smartAgentPackageName).then((module: unknown) => module as ILooseSmartAgentModule); return smartAgentModulePromise; }; const loadSmartAi = (): Promise => { smartAiModulePromise ??= import(smartAiPackageName).then((module: unknown) => module as ILooseSmartAiModule); return smartAiModulePromise; }; export class GitZoneAgentRuntime { private readonly sessionsByInstance = new Map>(); private readonly sessionLoadPromises = new Map>>(); constructor( private readonly emitEvent: TEmitAgentEvent, private readonly resolveProjectContext: TResolveProjectContext, private readonly persistenceRoot = path.join(os.homedir(), '.git.zone', 'ide', 'agent-sessions'), ) {} async listSessions(input: IAgentSessionInput): Promise { const instanceId = requireString(input.instanceId, 'Agent instance id'); return [...(await this.ensureInstanceSessions(instanceId)).values()].map(toSessionDescriptor); } async createSession(input: ICreateAgentSessionInput): Promise { const instanceId = requireString(input.instanceId, 'Agent instance id'); await this.ensureInstanceSessions(instanceId); const now = new Date().toISOString(); const session: IAgentSession = { id: randomId('agent'), instanceId, title: trimOptional(input.title) ?? 'Git.Zone IDE Session', createdAt: now, updatedAt: now, messages: [], status: { type: 'idle' }, pendingPermissions: new Map(), rememberedPermissions: new Set(), }; this.sessionsByInstance.get(instanceId)!.set(session.id, session); await this.persistInstanceSessions(instanceId); this.emit(instanceId, 'session.created', { session: toSessionDescriptor(session), sessionId: session.id }); return toSessionDescriptor(session); } async getMessages(input: IAgentSessionInput & { limit?: number }): Promise { const session = await this.requireSession(input); const limit = input.limit && Number.isInteger(input.limit) && input.limit > 0 ? input.limit : session.messages.length; return session.messages.slice(-limit); } async getSessionStatus(input: IAgentSessionInput): Promise> { const instanceId = requireString(input.instanceId, 'Agent instance id'); const result: Record = {}; for (const session of (await this.ensureInstanceSessions(instanceId)).values()) { result[session.id] = session.status; } return result; } getChildren(_input: IAgentSessionInput): unknown[] { return []; } async prompt(input: IAgentPromptInput): Promise { const session = await this.requireSession(input); const context = this.requireProjectContext(session.instanceId); const text = requireString(input.text, 'Prompt text'); if (session.abortController) { throw new Error('Agent session already has a running prompt.'); } const userMessage = this.addMessage(session, 'user', text); this.emit(session.instanceId, 'message.created', { sessionId: session.id, message: userMessage }); const assistantMessage = this.addMessage(session, 'assistant', ''); this.emit(session.instanceId, 'message.created', { sessionId: session.id, message: assistantMessage }); const abortController = new AbortController(); session.abortController = abortController; this.setStatus(session, { type: 'busy', message: 'Running SmartAgent' }); try { const smartagent = await loadSmartAgent(); const model = await this.createModel(); const result = await smartagent.runAgent({ model, prompt: text, system: createSystemPrompt(context), messages: toModelMessages(session.messages.filter((message) => message.id !== userMessage.id && message.id !== assistantMessage.id)), tools: this.createRemoteTools(smartagent, context, session), sessionId: session.id, maxSteps: 20, cache: 'auto', abort: abortController.signal, onToken: (delta: string) => { assistantMessage.text += delta; assistantMessage.updatedAt = new Date().toISOString(); this.emit(session.instanceId, 'message.updated', { sessionId: session.id, message: assistantMessage }); }, onToolCall: (toolName: string, toolInput: unknown) => { const toolMessage = this.addMessage(session, 'tool', `Tool: ${toolName}\n${formatJson(toolInput)}`); this.emit(session.instanceId, 'tool.started', { sessionId: session.id, message: toolMessage, toolName, input: toolInput }); }, onToolResult: (toolName: string, toolResult: unknown) => { const toolMessage = this.addMessage(session, 'tool', `Tool result: ${toolName}\n${formatJson(toolResult)}`); this.emit(session.instanceId, 'tool.finished', { sessionId: session.id, message: toolMessage, toolName, output: toolResult }); }, }); assistantMessage.text = result.text || assistantMessage.text; assistantMessage.updatedAt = new Date().toISOString(); session.updatedAt = assistantMessage.updatedAt; this.emit(session.instanceId, 'message.updated', { sessionId: session.id, message: assistantMessage }); this.setStatus(session, { type: 'idle' }); return assistantMessage; } catch (error) { if (abortController.signal.aborted) { assistantMessage.text = `${assistantMessage.text}\n\nStopped.`.trim(); this.setStatus(session, { type: 'idle' }); } else { const message = error instanceof Error ? error.message : String(error); assistantMessage.text = `${assistantMessage.text}\n\nAgent error: ${message}`.trim(); this.setStatus(session, { type: 'error', message }); } assistantMessage.updatedAt = new Date().toISOString(); this.emit(session.instanceId, 'message.updated', { sessionId: session.id, message: assistantMessage }); throw error; } finally { session.abortController = undefined; await this.persistInstanceSessions(session.instanceId); } } async abort(input: IAgentSessionInput): Promise<{ aborted: boolean }> { const session = await this.requireSession(input); if (!session.abortController) { return { aborted: false }; } session.abortController.abort(); this.rejectPendingPermissions(session, new Error('Agent run was stopped.')); return { aborted: true }; } async respondToPermission(input: IAgentPermissionResponseInput): Promise<{ accepted: boolean }> { const session = await this.requireSession(input); const permissionId = requireString(input.permissionId, 'Permission id'); const pending = session.pendingPermissions.get(permissionId); if (!pending) { return { accepted: false }; } session.pendingPermissions.delete(permissionId); const response = input.response ?? 'reject'; if (response === 'reject') { pending.reject(new Error('Permission rejected.')); this.emit(session.instanceId, 'permission.replied', { sessionId: session.id, requestID: permissionId, response }); return { accepted: true }; } if (response === 'always' || input.remember) { session.rememberedPermissions.add(pending.key); await this.persistInstanceSessions(session.instanceId); } pending.resolve(); this.emit(session.instanceId, 'permission.replied', { sessionId: session.id, requestID: permissionId, response }); return { accepted: true }; } dispose(): void { for (const instanceSessions of this.sessionsByInstance.values()) { for (const session of instanceSessions.values()) { session.abortController?.abort(); this.rejectPendingPermissions(session, new Error('Agent runtime is shutting down.')); } } } private async ensureInstanceSessions(instanceId: string): Promise> { const existing = this.sessionsByInstance.get(instanceId); if (existing) return existing; const existingLoad = this.sessionLoadPromises.get(instanceId); if (existingLoad) return existingLoad; const loadPromise = this.loadInstanceSessions(instanceId); this.sessionLoadPromises.set(instanceId, loadPromise); try { const sessions = await loadPromise; this.sessionsByInstance.set(instanceId, sessions); return sessions; } finally { this.sessionLoadPromises.delete(instanceId); } } private async requireSession(input: IAgentSessionInput): Promise { const instanceId = requireString(input.instanceId, 'Agent instance id'); const sessionId = requireString(input.sessionId, 'Agent session id'); const session = (await this.ensureInstanceSessions(instanceId)).get(sessionId); if (!session) { throw new Error(`Agent session not found: ${sessionId}`); } return session; } private requireProjectContext(instanceId: string): IAgentProjectContext { const context = this.resolveProjectContext(instanceId); if (!context) { throw new Error(`Project instance not found for agent runtime: ${instanceId}`); } return context; } private setStatus(session: IAgentSession, status: TAgentStatus): void { session.status = status; session.updatedAt = new Date().toISOString(); this.emit(session.instanceId, 'session.updated', { sessionId: session.id, status }); } private addMessage(session: IAgentSession, role: IAgentMessage['role'], text: string): IAgentMessage { const now = new Date().toISOString(); const message: IAgentMessage = { id: randomId('message'), role, text, createdAt: now, updatedAt: now, }; session.messages.push(message); session.updatedAt = now; return message; } private emit(instanceId: string, type: string, data?: Record): void { this.emitEvent({ instanceId, event: { type, data } }); } private async loadInstanceSessions(instanceId: string): Promise> { const context = this.requireProjectContext(instanceId); const sessions = new Map(); let parsed: IPersistedAgentSessionsFile; try { parsed = JSON.parse(await fs.readFile(this.persistenceFilePath(context), 'utf8')) as IPersistedAgentSessionsFile; } catch (error) { const nodeError = error as NodeJS.ErrnoException; if (nodeError.code === 'ENOENT') { return sessions; } throw error; } if (!Array.isArray(parsed.sessions)) { return sessions; } for (const persisted of parsed.sessions) { const session = normalizePersistedSession(instanceId, persisted); if (session) { sessions.set(session.id, session); } } return sessions; } private async persistInstanceSessions(instanceId: string): Promise { const context = this.requireProjectContext(instanceId); const sessions = this.sessionsByInstance.get(instanceId); if (!sessions) return; const filePath = this.persistenceFilePath(context); const payload: IPersistedAgentSessionsFile = { version: 1, project: { title: context.title, path: context.path, target: { hostAlias: context.target.hostAlias, hostName: context.target.hostName, user: context.target.user, port: context.target.port, }, }, sessions: [...sessions.values()].map(toPersistedSession), }; const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`; await fs.mkdir(path.dirname(filePath), { recursive: true, mode: 0o700 }); await fs.writeFile(tempPath, `${JSON.stringify(payload, undefined, 2)}\n`, { mode: 0o600 }); await fs.rename(tempPath, filePath); } private persistenceFilePath(context: IAgentProjectContext): string { return path.join(this.persistenceRoot, `${persistenceKeyForContext(context)}.json`); } private async createModel(): Promise { const smartai = await loadSmartAi(); const model = process.env.GITZONE_IDE_AGENT_MODEL || 'gpt-5.5'; const apiKey = process.env.OPENAI_API_KEY || process.env.OPENAI_TOKEN; if (apiKey) { return smartai.getModel({ provider: 'openai', model, apiKey }); } const openAiMaxAuth = await loadOpenAiMaxAuth(); if (openAiMaxAuth) { return smartai.getModel({ provider: 'openai', model, openAiMaxAuth }); } throw new Error('No OpenAI credentials available. Set OPENAI_API_KEY/OPENAI_TOKEN or sign in with ChatGPT using Codex device auth.'); } private createRemoteTools(smartagent: ILooseSmartAgentModule, context: IAgentProjectContext, session: IAgentSession): Record { return { run_command: smartagent.tool({ description: 'Run a shell command on the remote SSH host inside the active project directory. Requires user permission.', inputSchema: smartagent.z.object({ command: smartagent.z.string().describe('Shell command to run'), timeoutMs: smartagent.z.number().optional().describe('Timeout in milliseconds'), }), execute: async ({ command, timeoutMs }: { command: string; timeoutMs?: number }) => { await this.requirePermission(session, context, 'shell', 'Run remote command', { command }); const result = await runRemoteShellCommand(context, command, timeoutMs ?? 120000); return truncateRemoteResult(result, smartagent); }, }), list_directory: smartagent.tool({ description: 'List files in a directory under the active remote project. Paths may be relative to the project root.', inputSchema: smartagent.z.object({ path: smartagent.z.string().default('.').describe('Directory path'), }), execute: async ({ path: directoryPath }: { path: string }) => { const result = await runRemoteNodeTool(context, listDirectoryScript, { GITZONE_AGENT_PATH: directoryPath, }); return truncateRemoteResult(result, smartagent); }, }), read_file: smartagent.tool({ description: 'Read a UTF-8 file under the active remote project. Paths may be relative to the project root.', inputSchema: smartagent.z.object({ path: smartagent.z.string().describe('File path'), startLine: smartagent.z.number().optional().describe('First line, 1-indexed'), endLine: smartagent.z.number().optional().describe('Last line, 1-indexed'), }), execute: async ({ path: filePath, startLine, endLine }: { path: string; startLine?: number; endLine?: number }) => { const result = await runRemoteNodeTool(context, readFileScript, { GITZONE_AGENT_PATH: filePath, GITZONE_AGENT_START_LINE: startLine ? String(startLine) : '', GITZONE_AGENT_END_LINE: endLine ? String(endLine) : '', }); return truncateRemoteResult(result, smartagent); }, }), write_file: smartagent.tool({ description: 'Write a UTF-8 file under the active remote project. Requires user permission.', inputSchema: smartagent.z.object({ path: smartagent.z.string().describe('File path'), content: smartagent.z.string().describe('Complete file content to write'), }), execute: async ({ path: filePath, content }: { path: string; content: string }) => { await this.requirePermission(session, context, 'write', 'Write remote file', { path: filePath, bytes: Buffer.byteLength(content) }); const result = await runRemoteNodeTool(context, writeFileScript, { GITZONE_AGENT_PATH: filePath, GITZONE_AGENT_CONTENT_B64: Buffer.from(content, 'utf8').toString('base64'), }); return truncateRemoteResult(result, smartagent); }, }), }; } private async requirePermission( session: IAgentSession, _context: IAgentProjectContext, type: string, title: string, metadata: Record, ): Promise { const key = `${type}:${JSON.stringify(metadata)}`; if (session.rememberedPermissions.has(key)) { return; } const id = randomId('permission'); await new Promise((resolve, reject) => { session.pendingPermissions.set(id, { id, key, resolve, reject }); this.emit(session.instanceId, 'permission.asked', { requestID: id, sessionId: session.id, title, type, metadata, }); }); } private rejectPendingPermissions(session: IAgentSession, error: Error): void { for (const pending of session.pendingPermissions.values()) { pending.reject(error); this.emit(session.instanceId, 'permission.replied', { sessionId: session.id, requestID: pending.id, response: 'reject' }); } session.pendingPermissions.clear(); } } const toSessionDescriptor = (session: IAgentSession): IAgentSessionDescriptor => ({ id: session.id, title: session.title, createdAt: session.createdAt, updatedAt: session.updatedAt, }); const toPersistedSession = (session: IAgentSession): IPersistedAgentSession => ({ id: session.id, title: session.title, createdAt: session.createdAt, updatedAt: session.updatedAt, messages: session.messages, rememberedPermissions: [...session.rememberedPermissions], }); const normalizePersistedSession = (instanceId: string, persisted: IPersistedAgentSession): IAgentSession | undefined => { if (!persisted || typeof persisted !== 'object') return undefined; const id = stringValue(persisted.id); if (!id) return undefined; const now = new Date().toISOString(); return { id, instanceId, title: stringValue(persisted.title) ?? 'Git.Zone IDE Session', createdAt: stringValue(persisted.createdAt) ?? now, updatedAt: stringValue(persisted.updatedAt) ?? now, messages: Array.isArray(persisted.messages) ? persisted.messages.map(normalizePersistedMessage).filter((message): message is IAgentMessage => !!message) : [], status: { type: 'idle' }, pendingPermissions: new Map(), rememberedPermissions: new Set(Array.isArray(persisted.rememberedPermissions) ? persisted.rememberedPermissions.filter((item) => typeof item === 'string') : []), }; }; const normalizePersistedMessage = (message: IAgentMessage): IAgentMessage | undefined => { if (!message || typeof message !== 'object') return undefined; const id = stringValue(message.id); const role = message.role; const text = typeof message.text === 'string' ? message.text : undefined; const createdAt = stringValue(message.createdAt); if (!id || !isAgentMessageRole(role) || text === undefined || !createdAt) return undefined; return { id, role, text, createdAt, updatedAt: stringValue(message.updatedAt), }; }; const isAgentMessageRole = (role: unknown): role is IAgentMessage['role'] => { return role === 'user' || role === 'assistant' || role === 'tool'; }; const persistenceKeyForContext = (context: IAgentProjectContext): string => { return plugins.crypto .createHash('sha256') .update(JSON.stringify({ hostAlias: context.target.hostAlias, hostName: context.target.hostName, path: context.path, port: context.target.port, user: context.target.user, })) .digest('hex'); }; const toModelMessages = (messages: IAgentMessage[]): Array<{ role: 'user' | 'assistant'; content: string }> => { return messages .filter((message) => (message.role === 'user' || message.role === 'assistant') && message.text.trim()) .map((message) => ({ role: message.role as 'user' | 'assistant', content: message.text })); }; const createSystemPrompt = (context: IAgentProjectContext) => [ 'You are the Git.Zone IDE SmartAgent for a remote SSH workspace.', `Project title: ${context.title}`, `Remote project root: ${context.path}`, 'Use the provided remote tools for project inspection and changes. Do not assume local filesystem access is the remote workspace.', 'Prefer small, focused changes. Ask for clarification if the user request is ambiguous or risky.', ].join('\n'); const runRemoteShellCommand = async (context: IAgentProjectContext, command: string, timeoutMs: number) => { const remoteCommand = [ 'set -euo pipefail', `cd ${plugins.ideServerInstaller.quoteRemotePath(context.path)}`, command, ].join('\n'); return plugins.ideSsh.runSshCommand(context.target, remoteCommand, { batchMode: context.batchMode, timeoutMs, }); }; const runRemoteNodeTool = async ( context: IAgentProjectContext, script: string, env: Record, timeoutMs = 60000, ) => { const nodePath = plugins.ideServerInstaller.joinRemotePath(context.runtimeRoot, 'node/bin/node'); const nodeLibPath = plugins.ideServerInstaller.joinRemotePath(context.runtimeRoot, 'node/lib'); const remoteCommand = [ 'set -euo pipefail', `export LD_LIBRARY_PATH=${plugins.ideServerInstaller.quoteRemotePath(nodeLibPath)}:\${LD_LIBRARY_PATH:-}`, `export GITZONE_AGENT_ROOT=${plugins.ideServerInstaller.quoteRemotePath(context.path)}`, ...Object.entries(env).map(([key, value]) => `export ${key}=${plugins.ideServerInstaller.quoteShellArg(value)}`), `${plugins.ideServerInstaller.quoteRemotePath(nodePath)} <<'GITZONE_IDE_AGENT_NODE'`, script, 'GITZONE_IDE_AGENT_NODE', ].join('\n'); return plugins.ideSsh.runSshCommand(context.target, remoteCommand, { batchMode: context.batchMode, timeoutMs, }); }; const truncateRemoteResult = (result: plugins.ideSsh.ISshRunResult, smartagent: ILooseSmartAgentModule): string => { const output = result.exitCode === 0 ? result.stdout : `Exit code: ${result.exitCode}\nstdout:\n${result.stdout}\nstderr:\n${result.stderr}`; return smartagent.truncateOutput(output).content; }; const remotePathPrelude = ` const fs = require('fs'); const path = require('path'); const root = path.resolve(process.env.GITZONE_AGENT_ROOT || '.'); const requested = process.env.GITZONE_AGENT_PATH || '.'; const target = path.isAbsolute(requested) ? path.resolve(requested) : path.resolve(root, requested); if (target !== root && !target.startsWith(root + path.sep)) { throw new Error('Path is outside the active project root: ' + requested); } `; const listDirectoryScript = `${remotePathPrelude} const entries = fs.readdirSync(target, { withFileTypes: true }) .map((entry) => entry.name + (entry.isDirectory() ? '/' : '')) .sort(); console.log(entries.join('\n')); `; const readFileScript = `${remotePathPrelude} const text = fs.readFileSync(target, 'utf8'); const startLine = Number.parseInt(process.env.GITZONE_AGENT_START_LINE || '', 10); const endLine = Number.parseInt(process.env.GITZONE_AGENT_END_LINE || '', 10); if (Number.isFinite(startLine) || Number.isFinite(endLine)) { const lines = text.split('\n'); const start = Number.isFinite(startLine) && startLine > 0 ? startLine - 1 : 0; const end = Number.isFinite(endLine) && endLine > 0 ? endLine : lines.length; console.log(lines.slice(start, end).join('\n')); } else { console.log(text); } `; const writeFileScript = `${remotePathPrelude} const content = Buffer.from(process.env.GITZONE_AGENT_CONTENT_B64 || '', 'base64').toString('utf8'); fs.mkdirSync(path.dirname(target), { recursive: true }); fs.writeFileSync(target, content, 'utf8'); console.log('Written ' + Buffer.byteLength(content, 'utf8') + ' bytes to ' + path.relative(root, target)); `; const loadOpenAiMaxAuth = async (): Promise => { const smartai = await loadSmartAi(); const localAuthPath = path.join(os.homedir(), '.git.zone', 'ide', 'openai-max-auth.json'); const codexAuthPath = path.join(os.homedir(), '.codex', 'auth.json'); return await loadOpenAiMaxAuthFromPath(smartai, localAuthPath, true) ?? await loadOpenAiMaxAuthFromPath(smartai, codexAuthPath, false); }; const loadOpenAiMaxAuthFromPath = async (smartai: ILooseSmartAiModule, filePath: string, allowRefreshWrite: boolean): Promise => { try { const parsed = JSON.parse(await fs.readFile(filePath, 'utf8')) as Record; const tokenData = normalizeOpenAiMaxAuth(smartai, parsed); if (!tokenData) return undefined; if (allowRefreshWrite && shouldRefreshToken(tokenData)) { const refreshed = await smartai.refreshOpenAiMaxTokenData(tokenData); await fs.mkdir(path.dirname(filePath), { recursive: true, mode: 0o700 }); await fs.writeFile(filePath, `${JSON.stringify(refreshed, undefined, 2)}\n`, { mode: 0o600 }); return refreshed; } return tokenData; } catch (error) { const nodeError = error as NodeJS.ErrnoException; if (nodeError.code === 'ENOENT') return undefined; return undefined; } }; const normalizeOpenAiMaxAuth = (smartai: ILooseSmartAiModule, input: Record): IOpenAiMaxTokenData | undefined => { const tokens = (input.tokens && typeof input.tokens === 'object' ? input.tokens : input) as Record; const accessToken = stringValue(tokens.accessToken) ?? stringValue(tokens.access_token); const refreshToken = stringValue(tokens.refreshToken) ?? stringValue(tokens.refresh_token); const idToken = stringValue(tokens.idToken) ?? stringValue(tokens.id_token) ?? stringValue((tokens.idTokenInfo as Record | undefined)?.rawJwt); if (!accessToken || !refreshToken || !idToken) return undefined; const idTokenInfo = smartai.parseOpenAiMaxIdToken(idToken); return { accessToken, refreshToken, idToken, idTokenInfo, accountId: stringValue(tokens.accountId) ?? stringValue(tokens.account_id) ?? idTokenInfo.chatgptAccountId, }; }; const shouldRefreshToken = (tokenData: IOpenAiMaxTokenData): boolean => { if (!tokenData.idTokenInfo.expiresAt) return false; return Date.parse(tokenData.idTokenInfo.expiresAt) - Date.now() < 5 * 60 * 1000; }; const requireString = (value: string | undefined, label: string): string => { const trimmed = trimOptional(value); if (!trimmed) { throw new Error(`${label} is required.`); } return trimmed; }; const trimOptional = (value: string | undefined): string | undefined => { const trimmed = value?.trim(); return trimmed || undefined; }; const stringValue = (value: unknown): string | undefined => { return typeof value === 'string' && value ? value : undefined; }; const randomId = (prefix: string): string => `${prefix}-${plugins.crypto.randomBytes(12).toString('hex')}`; const formatJson = (value: unknown): string => { if (typeof value === 'string') return value; try { return JSON.stringify(value, undefined, 2); } catch { return String(value); } };