|
14 | 14 | * limitations under the License. |
15 | 15 | */ |
16 | 16 |
|
17 | | -import { readdir, readFile, unlink, writeFile } from 'node:fs/promises'; |
18 | | -import { join } from 'node:path'; |
19 | | -import { SfError } from '@salesforce/core'; |
20 | | -import type { SfProject } from '@salesforce/core'; |
21 | | -import type { ProductionAgent, ScriptAgent } from '@salesforce/agents'; |
22 | | - |
23 | | -const SESSION_META_FILE = 'session-meta.json'; |
24 | | - |
25 | | -export type SessionMeta = { displayName?: string }; |
26 | | - |
27 | | -/** |
28 | | - * Save a marker so send/end can validate that the session was started for this agent. |
29 | | - * Caller must have started the session (agent has sessionId set). Uses agent.getHistoryDir() for the path. |
30 | | - * Pass displayName (authoring bundle name or production agent API name) so "agent preview sessions" can show it. |
31 | | - */ |
32 | | -export async function createCache( |
33 | | - agent: ScriptAgent | ProductionAgent, |
34 | | - options?: { displayName?: string } |
35 | | -): Promise<void> { |
36 | | - const historyDir = await agent.getHistoryDir(); |
37 | | - const metaPath = join(historyDir, SESSION_META_FILE); |
38 | | - const meta: SessionMeta = { displayName: options?.displayName }; |
39 | | - await writeFile(metaPath, JSON.stringify(meta), 'utf-8'); |
40 | | -} |
41 | | - |
42 | | -/** |
43 | | - * Validate that the session was started for this agent (marker file exists in agent's history dir for current sessionId). |
44 | | - * Caller must set sessionId on the agent (agent.setSessionId) before calling. |
45 | | - * Throws SfError if the session marker is not found. |
46 | | - */ |
47 | | -export async function validatePreviewSession(agent: ScriptAgent | ProductionAgent): Promise<void> { |
48 | | - const historyDir = await agent.getHistoryDir(); |
49 | | - const metaPath = join(historyDir, SESSION_META_FILE); |
50 | | - try { |
51 | | - await readFile(metaPath, 'utf-8'); |
52 | | - } catch { |
53 | | - throw new SfError( |
54 | | - 'No preview session found for this session ID. Run "sf agent preview start" first.', |
55 | | - 'PreviewSessionNotFound' |
56 | | - ); |
57 | | - } |
58 | | -} |
59 | | - |
60 | | -/** |
61 | | - * Remove the session marker so this session is no longer considered "active" for send/end without --session-id. |
62 | | - * Call after ending the session. Caller must set sessionId on the agent before calling. |
63 | | - */ |
64 | | -export async function removeCache(agent: ScriptAgent | ProductionAgent): Promise<void> { |
65 | | - const historyDir = await agent.getHistoryDir(); |
66 | | - const metaPath = join(historyDir, SESSION_META_FILE); |
67 | | - try { |
68 | | - await unlink(metaPath); |
69 | | - } catch { |
70 | | - // already removed or never created |
71 | | - } |
72 | | -} |
73 | | - |
74 | | -/** |
75 | | - * List session IDs that have a cache marker (started via "agent preview start") for this agent. |
76 | | - * Uses project path and agent's storage ID to find .sfdx/agents/<agentId>/sessions/<sessionId>/session-meta.json. |
77 | | - */ |
78 | | -export async function getCachedSessionIds(project: SfProject, agent: ScriptAgent | ProductionAgent): Promise<string[]> { |
79 | | - const agentId = agent.getAgentIdForStorage(); |
80 | | - const base = join(project.getPath(), '.sfdx'); |
81 | | - const sessionsDir = join(base, 'agents', agentId, 'sessions'); |
82 | | - const sessionIds: string[] = []; |
83 | | - try { |
84 | | - const entries = await readdir(sessionsDir, { withFileTypes: true }); |
85 | | - const dirs = entries.filter((e) => e.isDirectory()).map((e) => e.name); |
86 | | - const hasMarker = await Promise.all( |
87 | | - dirs.map(async (name) => { |
88 | | - try { |
89 | | - await readFile(join(sessionsDir, name, SESSION_META_FILE), 'utf-8'); |
90 | | - return true; |
91 | | - } catch { |
92 | | - return false; |
93 | | - } |
94 | | - }) |
95 | | - ); |
96 | | - dirs.forEach((name, i) => { |
97 | | - if (hasMarker[i]) sessionIds.push(name); |
98 | | - }); |
99 | | - } catch { |
100 | | - // sessions dir missing or unreadable |
101 | | - } |
102 | | - return sessionIds; |
103 | | -} |
104 | | - |
105 | | -/** |
106 | | - * Return the single "current" session ID when safe: exactly one cached session for this agent. |
107 | | - * Returns undefined when there are zero or multiple sessions (caller should require --session-id). |
108 | | - */ |
109 | | -export async function getCurrentSessionId( |
110 | | - project: SfProject, |
111 | | - agent: ScriptAgent | ProductionAgent |
112 | | -): Promise<string | undefined> { |
113 | | - const ids = await getCachedSessionIds(project, agent); |
114 | | - return ids.length === 1 ? ids[0] : undefined; |
115 | | -} |
116 | | - |
117 | | -export type CachedSessionEntry = { agentId: string; displayName?: string; sessionIds: string[] }; |
118 | | - |
119 | | -/** |
120 | | - * List all cached preview sessions in the project, grouped by agent ID. |
121 | | - * displayName (when present in session-meta.json) is the authoring bundle name or production agent API name for display. |
122 | | - * Use this to show users which sessions exist so they can end or clean up. |
123 | | - */ |
124 | | -export async function listCachedSessions(project: SfProject): Promise<CachedSessionEntry[]> { |
125 | | - const base = join(project.getPath(), '.sfdx', 'agents'); |
126 | | - const result: CachedSessionEntry[] = []; |
127 | | - try { |
128 | | - const agentDirs = await readdir(base, { withFileTypes: true }); |
129 | | - const entries = await Promise.all( |
130 | | - agentDirs |
131 | | - .filter((ent) => ent.isDirectory()) |
132 | | - .map(async (ent) => { |
133 | | - const agentId = ent.name; |
134 | | - const sessionsDir = join(base, agentId, 'sessions'); |
135 | | - let sessionIds: string[] = []; |
136 | | - let displayName: string | undefined; |
137 | | - try { |
138 | | - const sessionDirs = await readdir(sessionsDir, { withFileTypes: true }); |
139 | | - const withMarker = await Promise.all( |
140 | | - sessionDirs |
141 | | - .filter((s) => s.isDirectory()) |
142 | | - .map(async (s) => { |
143 | | - try { |
144 | | - await readFile(join(sessionsDir, s.name, SESSION_META_FILE), 'utf-8'); |
145 | | - return s.name; |
146 | | - } catch { |
147 | | - return null; |
148 | | - } |
149 | | - }) |
150 | | - ); |
151 | | - sessionIds = withMarker.filter((id): id is string => id !== null); |
152 | | - if (sessionIds.length > 0) { |
153 | | - try { |
154 | | - const raw = await readFile(join(sessionsDir, sessionIds[0], SESSION_META_FILE), 'utf-8'); |
155 | | - const meta = JSON.parse(raw) as SessionMeta; |
156 | | - displayName = meta.displayName; |
157 | | - } catch { |
158 | | - // ignore |
159 | | - } |
160 | | - } |
161 | | - } catch { |
162 | | - // no sessions dir or unreadable |
163 | | - } |
164 | | - return { agentId, displayName, sessionIds }; |
165 | | - }) |
166 | | - ); |
167 | | - result.push(...entries.filter((e) => e.sessionIds.length > 0)); |
168 | | - } catch { |
169 | | - // no agents dir or unreadable |
170 | | - } |
171 | | - return result; |
172 | | -} |
| 17 | +export { |
| 18 | + createPreviewSessionCache as createCache, |
| 19 | + validatePreviewSession, |
| 20 | + removePreviewSessionCache as removeCache, |
| 21 | + getCachedPreviewSessionIds as getCachedSessionIds, |
| 22 | + getCurrentPreviewSessionId as getCurrentSessionId, |
| 23 | + listCachedPreviewSessions as listCachedSessions, |
| 24 | + type SessionType, |
| 25 | + type PreviewSessionMeta, |
| 26 | + type CachedPreviewSessionInfo, |
| 27 | + type CachedPreviewSessionEntry, |
| 28 | +} from '@salesforce/agents'; |
0 commit comments