docs-combiner 0.4.0 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agentApi/agentControlClaim.js +26 -0
- package/dist/agentApi/approachMatrixView.js +19 -0
- package/dist/agentApi/capabilitiesMeta.js +78 -0
- package/dist/agentApi/catalogUrlPresets.js +26 -0
- package/dist/agentApi/constants.js +11 -0
- package/dist/agentApi/creativeSelections.js +25 -0
- package/dist/agentApi/escalation.js +221 -0
- package/dist/agentApi/generatedPairsGuard.js +85 -0
- package/dist/agentApi/httpHelpers.js +77 -0
- package/dist/agentApi/imagesReadiness.js +60 -0
- package/dist/agentApi/jobStatus.js +113 -0
- package/dist/agentApi/productImageDrive.js +39 -0
- package/dist/agentApi/regenerateImages.js +125 -0
- package/dist/agentApi/rendererTypes.js +3 -0
- package/dist/agentApi/routes.js +440 -0
- package/dist/agentApi/sessionSnapshot.js +82 -0
- package/dist/agentApi/sessionTypes.js +2 -0
- package/dist/agentApi/spec.js +1045 -0
- package/dist/agentApi/types.js +2 -0
- package/dist/agentApi/validation.js +32 -0
- package/dist/campaignsCsv.js +420 -0
- package/dist/contentPairs.js +62 -0
- package/dist/flexcardBalance.js +88 -0
- package/dist/integrations/driveApi.js +420 -0
- package/dist/integrations/httpTimeout.js +116 -0
- package/dist/integrations/openrouter.js +532 -0
- package/dist/main.js +316 -1214
- package/dist/models.js +17 -0
- package/dist/offerSettings.js +19 -0
- package/dist/preload.js +9 -0
- package/dist/promptOverrides.js +437 -0
- package/dist/prompts.js +1243 -0
- package/dist/renderer.js +19 -19
- package/dist/renderer.js.map +1 -1
- package/dist/server/app.js +378 -0
- package/dist/server/auth.js +74 -0
- package/dist/server/contentJobs.js +367 -0
- package/dist/server/driveCatalog.js +122 -0
- package/dist/server/driveProxy.js +220 -0
- package/dist/server/flexcardMeta.js +29 -0
- package/dist/server/googleAuth.js +84 -0
- package/dist/server/imageAssets.js +87 -0
- package/dist/server/imageJobs.js +776 -0
- package/dist/server/index.js +38 -0
- package/dist/server/jobEvents.js +116 -0
- package/dist/server/jobs.js +358 -0
- package/dist/server/openRouterMeta.js +58 -0
- package/dist/server/spec.js +544 -0
- package/dist/server/storage.js +133 -0
- package/dist/server/telegram.js +53 -0
- package/dist/server/workspaceSnapshot.js +273 -0
- package/package.json +15 -3
package/dist/main.js
CHANGED
|
@@ -51,6 +51,8 @@ const path = __importStar(require("path"));
|
|
|
51
51
|
const fs = __importStar(require("fs"));
|
|
52
52
|
const http = __importStar(require("http"));
|
|
53
53
|
const url = __importStar(require("url"));
|
|
54
|
+
const routes_1 = require("./agentApi/routes");
|
|
55
|
+
const agentControlClaim_1 = require("./agentApi/agentControlClaim");
|
|
54
56
|
let pendingGoogleAuth = null;
|
|
55
57
|
let mainWindow = null;
|
|
56
58
|
let agentApiServer = null;
|
|
@@ -61,6 +63,8 @@ const windowFolderPaths = new Map();
|
|
|
61
63
|
const windowWorkspaces = new Map();
|
|
62
64
|
const windowTitles = new Map();
|
|
63
65
|
const closingWindowIds = new Set();
|
|
66
|
+
const closedWorkspaceIds = new Set();
|
|
67
|
+
const agentControlledSessions = new Set();
|
|
64
68
|
const AGENT_API_HOST = '127.0.0.1';
|
|
65
69
|
const DEFAULT_AGENT_API_PORT = 17321;
|
|
66
70
|
const APP_WINDOW_TITLE = 'Комбайнер';
|
|
@@ -106,41 +110,87 @@ function getWorkspaceWindows() {
|
|
|
106
110
|
};
|
|
107
111
|
});
|
|
108
112
|
}
|
|
109
|
-
function
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
generate: `${sessionUrl}/generate`,
|
|
140
|
-
},
|
|
141
|
-
};
|
|
113
|
+
function isSessionAgentControlled(sessionId) {
|
|
114
|
+
return agentControlledSessions.has(sessionId.trim());
|
|
115
|
+
}
|
|
116
|
+
function setSessionAgentControlMode(sessionId, mode) {
|
|
117
|
+
const id = sessionId.trim();
|
|
118
|
+
if (!id)
|
|
119
|
+
return;
|
|
120
|
+
if (mode === 'agent') {
|
|
121
|
+
agentControlledSessions.add(id);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
agentControlledSessions.delete(id);
|
|
125
|
+
}
|
|
126
|
+
const win = getWindowByWorkspaceId(id);
|
|
127
|
+
if (win && !win.isDestroyed() && !win.webContents.isDestroyed()) {
|
|
128
|
+
win.webContents.send('agent-control-mode-changed', { mode });
|
|
129
|
+
syncWindowTitleForAgentControl(win, id);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function isAgentApiReadyInSession(sessionId) {
|
|
133
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
134
|
+
const win = getWindowByWorkspaceId(sessionId);
|
|
135
|
+
if (!win || win.isDestroyed() || win.webContents.isDestroyed())
|
|
136
|
+
return false;
|
|
137
|
+
try {
|
|
138
|
+
return (yield win.webContents.executeJavaScript('Boolean(window.__docsCombinerAgentApi)', true)) === true;
|
|
139
|
+
}
|
|
140
|
+
catch (_a) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
142
143
|
});
|
|
143
144
|
}
|
|
145
|
+
function syncWindowTitleForAgentControl(win, workspaceId) {
|
|
146
|
+
const webContentsId = win.webContents.id;
|
|
147
|
+
const folderLabel = windowTitles.get(webContentsId) || 'Новый оффер';
|
|
148
|
+
const agentPrefix = isSessionAgentControlled(workspaceId) ? '🤖 ' : '';
|
|
149
|
+
win.setTitle(`${agentPrefix}${APP_WINDOW_TITLE} — ${folderLabel}`);
|
|
150
|
+
}
|
|
151
|
+
function buildAgentWorkSessionInfo(win) {
|
|
152
|
+
const baseUrl = getAgentApiBaseUrl();
|
|
153
|
+
const sessionUrl = `${baseUrl}/api/windows/${encodeURIComponent(win.workspaceId)}`;
|
|
154
|
+
const details = [
|
|
155
|
+
win.title,
|
|
156
|
+
win.folderPath ? `Drive folder: ${win.folderPath}` : 'No Drive folder selected',
|
|
157
|
+
win.active ? 'active session' : 'background session',
|
|
158
|
+
isSessionAgentControlled(win.workspaceId) ? 'agent-controlled' : 'manual control',
|
|
159
|
+
];
|
|
160
|
+
return {
|
|
161
|
+
id: win.workspaceId,
|
|
162
|
+
title: win.title,
|
|
163
|
+
description: details.join('; '),
|
|
164
|
+
folderId: win.folderId,
|
|
165
|
+
folderPath: win.folderPath,
|
|
166
|
+
active: win.active,
|
|
167
|
+
agentControlled: isSessionAgentControlled(win.workspaceId),
|
|
168
|
+
url: sessionUrl,
|
|
169
|
+
endpoints: {
|
|
170
|
+
info: sessionUrl,
|
|
171
|
+
update: sessionUrl,
|
|
172
|
+
setupCampaign: `${sessionUrl}/setup-campaign`,
|
|
173
|
+
geoBlock: `${sessionUrl}/geo-block`,
|
|
174
|
+
creativeSelections: `${sessionUrl}/creative-selections`,
|
|
175
|
+
approachMatrix: `${sessionUrl}/approach-matrix`,
|
|
176
|
+
capabilities: `${sessionUrl}/capabilities`,
|
|
177
|
+
validation: `${sessionUrl}/validation`,
|
|
178
|
+
generated: `${sessionUrl}/generated`,
|
|
179
|
+
logs: `${sessionUrl}/logs`,
|
|
180
|
+
jobs: `${sessionUrl}/jobs`,
|
|
181
|
+
createCampaignGroup: `${sessionUrl}/catalog/groups`,
|
|
182
|
+
createWorkspaceFolder: `${sessionUrl}/catalog/workspaces`,
|
|
183
|
+
generate: `${sessionUrl}/generate`,
|
|
184
|
+
agentControl: `${sessionUrl}/agent-control`,
|
|
185
|
+
actions: `${sessionUrl}/actions`,
|
|
186
|
+
regeneratePair: `${sessionUrl}/actions/regenerate-pair`,
|
|
187
|
+
regenerateImages: `${sessionUrl}/actions/regenerate-images`,
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function getAgentWorkSessions() {
|
|
192
|
+
return getWorkspaceWindows().map(buildAgentWorkSessionInfo);
|
|
193
|
+
}
|
|
144
194
|
function getWindowByWorkspaceId(workspaceId) {
|
|
145
195
|
var _a;
|
|
146
196
|
const id = workspaceId.trim();
|
|
@@ -148,12 +198,139 @@ function getWindowByWorkspaceId(workspaceId) {
|
|
|
148
198
|
!win.webContents.isDestroyed() &&
|
|
149
199
|
windowWorkspaces.get(win.webContents.id) === id)) !== null && _a !== void 0 ? _a : null;
|
|
150
200
|
}
|
|
151
|
-
function
|
|
201
|
+
function removeWorkspaceSnapshot(workspaceId) {
|
|
202
|
+
try {
|
|
203
|
+
const configPath = path.join(electron_1.app.getPath('userData'), 'config.json');
|
|
204
|
+
if (!fs.existsSync(configPath))
|
|
205
|
+
return;
|
|
206
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
207
|
+
const current = Array.isArray(config.workspaceSnapshots) ? config.workspaceSnapshots : [];
|
|
208
|
+
const next = current.filter((item) => (item === null || item === void 0 ? void 0 : item.id) !== workspaceId);
|
|
209
|
+
if (next.length !== current.length) {
|
|
210
|
+
fs.writeFileSync(configPath, JSON.stringify(Object.assign(Object.assign({}, config), { workspaceSnapshots: next })));
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
catch (_a) {
|
|
214
|
+
// ignore snapshot cleanup errors
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
function clearAllWorkspaceSnapshots() {
|
|
218
|
+
try {
|
|
219
|
+
const configPath = path.join(electron_1.app.getPath('userData'), 'config.json');
|
|
220
|
+
if (!fs.existsSync(configPath))
|
|
221
|
+
return;
|
|
222
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
223
|
+
const nextConfig = Object.assign(Object.assign({}, config), { workspaceSnapshots: [] });
|
|
224
|
+
fs.writeFileSync(configPath, JSON.stringify(nextConfig));
|
|
225
|
+
broadcastConfigUpdated(nextConfig);
|
|
226
|
+
}
|
|
227
|
+
catch (_a) {
|
|
228
|
+
// ignore snapshot cleanup errors
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
function pickWindowToKeep(allWindows) {
|
|
232
|
+
var _a, _b;
|
|
233
|
+
if (allWindows.length === 0)
|
|
234
|
+
return null;
|
|
235
|
+
return ((_b = (_a = electron_1.BrowserWindow.getFocusedWindow()) !== null && _a !== void 0 ? _a : (mainWindow && !mainWindow.isDestroyed() ? mainWindow : null)) !== null && _b !== void 0 ? _b : allWindows[0]);
|
|
236
|
+
}
|
|
237
|
+
function resetWindowToEmptySession(win) {
|
|
238
|
+
const webContentsId = win.webContents.id;
|
|
239
|
+
const oldWorkspaceId = windowWorkspaces.get(webContentsId);
|
|
240
|
+
if (oldWorkspaceId) {
|
|
241
|
+
agentControlledSessions.delete(oldWorkspaceId);
|
|
242
|
+
closedWorkspaceIds.add(oldWorkspaceId);
|
|
243
|
+
removeWorkspaceSnapshot(oldWorkspaceId);
|
|
244
|
+
}
|
|
245
|
+
cleanupWindowFolder(webContentsId);
|
|
246
|
+
const newWorkspaceId = createWorkspaceId();
|
|
247
|
+
closedWorkspaceIds.delete(newWorkspaceId);
|
|
248
|
+
windowWorkspaces.set(webContentsId, newWorkspaceId);
|
|
249
|
+
windowTitles.set(webContentsId, 'Новый оффер');
|
|
250
|
+
syncWindowTitleForAgentControl(win, newWorkspaceId);
|
|
251
|
+
mainWindow = win;
|
|
252
|
+
win.loadFile(path.join(__dirname, 'index.html'), {
|
|
253
|
+
query: {
|
|
254
|
+
workspaceId: newWorkspaceId,
|
|
255
|
+
agentControlled: '0',
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
function closeAllWorkspaceSessions() {
|
|
260
|
+
const allWindows = electron_1.BrowserWindow.getAllWindows().filter(win => !win.isDestroyed());
|
|
261
|
+
const keepWin = pickWindowToKeep(allWindows);
|
|
262
|
+
if (!keepWin) {
|
|
263
|
+
createWindow();
|
|
264
|
+
return { ok: true };
|
|
265
|
+
}
|
|
266
|
+
const keepId = keepWin.webContents.id;
|
|
267
|
+
clearAllWorkspaceSnapshots();
|
|
268
|
+
for (const win of allWindows) {
|
|
269
|
+
if (win.webContents.id === keepId)
|
|
270
|
+
continue;
|
|
271
|
+
const workspaceId = windowWorkspaces.get(win.webContents.id);
|
|
272
|
+
if (workspaceId) {
|
|
273
|
+
closeSessionByWorkspaceId(workspaceId);
|
|
274
|
+
continue;
|
|
275
|
+
}
|
|
276
|
+
const webContentsId = win.webContents.id;
|
|
277
|
+
closingWindowIds.add(webContentsId);
|
|
278
|
+
broadcastWorkspaceWindowsUpdated();
|
|
279
|
+
win.destroy();
|
|
280
|
+
setImmediate(() => broadcastWorkspaceWindowsUpdated());
|
|
281
|
+
}
|
|
282
|
+
resetWindowToEmptySession(keepWin);
|
|
283
|
+
broadcastWorkspaceWindowsUpdated();
|
|
284
|
+
return { ok: true };
|
|
285
|
+
}
|
|
286
|
+
function closeOtherWorkspaceSessions(exceptWebContentsId) {
|
|
287
|
+
const keepId = Number(exceptWebContentsId);
|
|
288
|
+
const allWindows = electron_1.BrowserWindow.getAllWindows().filter(win => !win.isDestroyed());
|
|
289
|
+
for (const win of allWindows) {
|
|
290
|
+
const webContentsId = win.webContents.id;
|
|
291
|
+
if (webContentsId === keepId)
|
|
292
|
+
continue;
|
|
293
|
+
const workspaceId = windowWorkspaces.get(webContentsId);
|
|
294
|
+
if (workspaceId) {
|
|
295
|
+
closeSessionByWorkspaceId(workspaceId);
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
closingWindowIds.add(webContentsId);
|
|
299
|
+
broadcastWorkspaceWindowsUpdated();
|
|
300
|
+
win.destroy();
|
|
301
|
+
setImmediate(() => broadcastWorkspaceWindowsUpdated());
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return { ok: true };
|
|
305
|
+
}
|
|
306
|
+
function closeSessionByWorkspaceId(workspaceId) {
|
|
307
|
+
const id = workspaceId.trim();
|
|
308
|
+
if (!id) {
|
|
309
|
+
return { ok: false, error: 'Session id is required.' };
|
|
310
|
+
}
|
|
311
|
+
const win = getWindowByWorkspaceId(id);
|
|
312
|
+
if (!win || win.isDestroyed()) {
|
|
313
|
+
return { ok: false, error: 'Session not found.' };
|
|
314
|
+
}
|
|
315
|
+
const webContentsId = win.webContents.id;
|
|
316
|
+
agentControlledSessions.delete(id);
|
|
317
|
+
closedWorkspaceIds.add(id);
|
|
318
|
+
removeWorkspaceSnapshot(id);
|
|
319
|
+
closingWindowIds.add(webContentsId);
|
|
320
|
+
broadcastWorkspaceWindowsUpdated();
|
|
321
|
+
win.destroy();
|
|
322
|
+
setImmediate(() => broadcastWorkspaceWindowsUpdated());
|
|
323
|
+
return { ok: true };
|
|
324
|
+
}
|
|
325
|
+
function callAgentRendererApi(workspaceId, method, payload, options) {
|
|
152
326
|
return __awaiter(this, void 0, void 0, function* () {
|
|
153
327
|
const win = getWindowByWorkspaceId(workspaceId);
|
|
154
328
|
if (!win) {
|
|
155
329
|
return { ok: false, status: 404, error: 'Session not found.' };
|
|
156
330
|
}
|
|
331
|
+
if ((0, agentControlClaim_1.shouldClaimAgentControl)(method, options)) {
|
|
332
|
+
setSessionAgentControlMode(workspaceId, 'agent');
|
|
333
|
+
}
|
|
157
334
|
try {
|
|
158
335
|
const script = `
|
|
159
336
|
(async () => {
|
|
@@ -190,9 +367,10 @@ function callAgentRendererApi(workspaceId, method, payload) {
|
|
|
190
367
|
}
|
|
191
368
|
function callFirstReadyAgentRendererApi(method, payload, preferredSessionId) {
|
|
192
369
|
return __awaiter(this, void 0, void 0, function* () {
|
|
370
|
+
const readOnlyOptions = { claimAgentControl: false };
|
|
193
371
|
const preferredId = String(preferredSessionId !== null && preferredSessionId !== void 0 ? preferredSessionId : '').trim();
|
|
194
372
|
if (preferredId) {
|
|
195
|
-
const preferred = yield callAgentRendererApi(preferredId, method, payload);
|
|
373
|
+
const preferred = yield callAgentRendererApi(preferredId, method, payload, readOnlyOptions);
|
|
196
374
|
if (preferred.ok)
|
|
197
375
|
return preferred;
|
|
198
376
|
}
|
|
@@ -200,7 +378,7 @@ function callFirstReadyAgentRendererApi(method, payload, preferredSessionId) {
|
|
|
200
378
|
if (focused && !focused.isDestroyed() && !focused.webContents.isDestroyed()) {
|
|
201
379
|
const focusedId = windowWorkspaces.get(focused.webContents.id);
|
|
202
380
|
if (focusedId) {
|
|
203
|
-
const focusedResponse = yield callAgentRendererApi(focusedId, method, payload);
|
|
381
|
+
const focusedResponse = yield callAgentRendererApi(focusedId, method, payload, readOnlyOptions);
|
|
204
382
|
if (focusedResponse.ok)
|
|
205
383
|
return focusedResponse;
|
|
206
384
|
}
|
|
@@ -213,7 +391,7 @@ function callFirstReadyAgentRendererApi(method, payload, preferredSessionId) {
|
|
|
213
391
|
const sessionId = windowWorkspaces.get(win.webContents.id);
|
|
214
392
|
if (!sessionId)
|
|
215
393
|
continue;
|
|
216
|
-
const response = yield callAgentRendererApi(sessionId, method, payload);
|
|
394
|
+
const response = yield callAgentRendererApi(sessionId, method, payload, readOnlyOptions);
|
|
217
395
|
if (response.ok)
|
|
218
396
|
return response;
|
|
219
397
|
}
|
|
@@ -236,1158 +414,29 @@ function waitForAgentSessionReady(sessionId_1) {
|
|
|
236
414
|
return null;
|
|
237
415
|
});
|
|
238
416
|
}
|
|
239
|
-
function getAgentApiSpec(port) {
|
|
240
|
-
return {
|
|
241
|
-
openapi: '3.1.0',
|
|
242
|
-
info: {
|
|
243
|
-
title: 'Docs Combiner Agent API',
|
|
244
|
-
version: electron_1.app.getVersion(),
|
|
245
|
-
description: 'Local-only API for automation agents controlling Docs Combiner.',
|
|
246
|
-
},
|
|
247
|
-
servers: [
|
|
248
|
-
{
|
|
249
|
-
url: `http://${AGENT_API_HOST}:${port}`,
|
|
250
|
-
description: 'Loopback-only Electron main process API',
|
|
251
|
-
},
|
|
252
|
-
],
|
|
253
|
-
paths: {
|
|
254
|
-
'/api/windows': {
|
|
255
|
-
get: {
|
|
256
|
-
operationId: 'listWindows',
|
|
257
|
-
summary: 'List open Docs Combiner work sessions',
|
|
258
|
-
responses: {
|
|
259
|
-
'200': {
|
|
260
|
-
description: 'Current work sessions available to an automation agent',
|
|
261
|
-
content: {
|
|
262
|
-
'application/json': {
|
|
263
|
-
schema: {
|
|
264
|
-
type: 'object',
|
|
265
|
-
required: ['ok', 'windows'],
|
|
266
|
-
properties: {
|
|
267
|
-
ok: { type: 'boolean', const: true },
|
|
268
|
-
windows: {
|
|
269
|
-
type: 'array',
|
|
270
|
-
items: { $ref: '#/components/schemas/AgentWorkSessionInfo' },
|
|
271
|
-
},
|
|
272
|
-
},
|
|
273
|
-
},
|
|
274
|
-
},
|
|
275
|
-
},
|
|
276
|
-
},
|
|
277
|
-
},
|
|
278
|
-
},
|
|
279
|
-
post: {
|
|
280
|
-
operationId: 'createWindow',
|
|
281
|
-
summary: 'Open a new Docs Combiner work session (Electron window)',
|
|
282
|
-
requestBody: {
|
|
283
|
-
required: false,
|
|
284
|
-
content: {
|
|
285
|
-
'application/json': {
|
|
286
|
-
schema: {
|
|
287
|
-
type: 'object',
|
|
288
|
-
properties: {
|
|
289
|
-
workspaceId: {
|
|
290
|
-
type: 'string',
|
|
291
|
-
description: 'Optional stable session id. Generated if omitted.',
|
|
292
|
-
},
|
|
293
|
-
},
|
|
294
|
-
},
|
|
295
|
-
},
|
|
296
|
-
},
|
|
297
|
-
},
|
|
298
|
-
responses: {
|
|
299
|
-
'201': {
|
|
300
|
-
description: 'New session created and Agent API is ready in the renderer',
|
|
301
|
-
},
|
|
302
|
-
'202': {
|
|
303
|
-
description: 'New window opened, but renderer Agent API is not ready yet',
|
|
304
|
-
},
|
|
305
|
-
},
|
|
306
|
-
},
|
|
307
|
-
},
|
|
308
|
-
'/api/catalog': {
|
|
309
|
-
get: {
|
|
310
|
-
operationId: 'getDriveCatalog',
|
|
311
|
-
summary: 'List offers, campaign groups and workspace folders from Google Drive',
|
|
312
|
-
parameters: [
|
|
313
|
-
{
|
|
314
|
-
name: 'sessionId',
|
|
315
|
-
in: 'query',
|
|
316
|
-
required: false,
|
|
317
|
-
schema: { type: 'string' },
|
|
318
|
-
description: 'Prefer data from this session if available.',
|
|
319
|
-
},
|
|
320
|
-
],
|
|
321
|
-
responses: {
|
|
322
|
-
'200': { description: 'Drive catalog snapshot' },
|
|
323
|
-
'503': { description: 'No session with Agent API ready' },
|
|
324
|
-
},
|
|
325
|
-
},
|
|
326
|
-
},
|
|
327
|
-
'/api/windows/{sessionId}': {
|
|
328
|
-
get: {
|
|
329
|
-
operationId: 'getSessionInfo',
|
|
330
|
-
summary: 'Return all agent-visible information from one work session',
|
|
331
|
-
parameters: [
|
|
332
|
-
{
|
|
333
|
-
name: 'sessionId',
|
|
334
|
-
in: 'path',
|
|
335
|
-
required: true,
|
|
336
|
-
schema: { type: 'string' },
|
|
337
|
-
},
|
|
338
|
-
],
|
|
339
|
-
responses: {
|
|
340
|
-
'200': {
|
|
341
|
-
description: 'Current live session state',
|
|
342
|
-
content: {
|
|
343
|
-
'application/json': {
|
|
344
|
-
schema: {
|
|
345
|
-
type: 'object',
|
|
346
|
-
required: ['ok', 'session'],
|
|
347
|
-
properties: {
|
|
348
|
-
ok: { type: 'boolean', const: true },
|
|
349
|
-
session: { $ref: '#/components/schemas/AgentSessionInfo' },
|
|
350
|
-
},
|
|
351
|
-
},
|
|
352
|
-
},
|
|
353
|
-
},
|
|
354
|
-
},
|
|
355
|
-
'404': { description: 'Session not found' },
|
|
356
|
-
'409': { description: 'Session renderer is not ready for agent calls' },
|
|
357
|
-
},
|
|
358
|
-
},
|
|
359
|
-
patch: {
|
|
360
|
-
operationId: 'patchSessionInfo',
|
|
361
|
-
summary: 'Partially update one work session without touching omitted fields',
|
|
362
|
-
parameters: [
|
|
363
|
-
{
|
|
364
|
-
name: 'sessionId',
|
|
365
|
-
in: 'path',
|
|
366
|
-
required: true,
|
|
367
|
-
schema: { type: 'string' },
|
|
368
|
-
},
|
|
369
|
-
],
|
|
370
|
-
requestBody: {
|
|
371
|
-
required: true,
|
|
372
|
-
content: {
|
|
373
|
-
'application/json': {
|
|
374
|
-
schema: { $ref: '#/components/schemas/PatchSessionInfoRequest' },
|
|
375
|
-
examples: {
|
|
376
|
-
productOnly: {
|
|
377
|
-
value: { generation: { product: 'Detoxil Water' } },
|
|
378
|
-
},
|
|
379
|
-
firstGeoOnly: {
|
|
380
|
-
value: { generation: { firstGeo: 'Romania' } },
|
|
381
|
-
},
|
|
382
|
-
geoPriceLinkPartial: {
|
|
383
|
-
value: {
|
|
384
|
-
generation: {
|
|
385
|
-
geoBlock: [
|
|
386
|
-
{ id: 'market-1', link: 'https://example.com/ro/' },
|
|
387
|
-
],
|
|
388
|
-
},
|
|
389
|
-
},
|
|
390
|
-
},
|
|
391
|
-
creativeSelectionsOnly: {
|
|
392
|
-
value: {
|
|
393
|
-
creativeSelections: {
|
|
394
|
-
selectedTextApproachNumbers: [1, 2, 3],
|
|
395
|
-
},
|
|
396
|
-
},
|
|
397
|
-
},
|
|
398
|
-
},
|
|
399
|
-
},
|
|
400
|
-
},
|
|
401
|
-
},
|
|
402
|
-
responses: {
|
|
403
|
-
'200': {
|
|
404
|
-
description: 'Session was partially updated',
|
|
405
|
-
content: {
|
|
406
|
-
'application/json': {
|
|
407
|
-
schema: {
|
|
408
|
-
type: 'object',
|
|
409
|
-
required: ['ok', 'session'],
|
|
410
|
-
properties: {
|
|
411
|
-
ok: { type: 'boolean', const: true },
|
|
412
|
-
session: { $ref: '#/components/schemas/AgentSessionInfo' },
|
|
413
|
-
},
|
|
414
|
-
},
|
|
415
|
-
},
|
|
416
|
-
},
|
|
417
|
-
},
|
|
418
|
-
'400': { description: 'Invalid partial update payload' },
|
|
419
|
-
'404': { description: 'Session not found' },
|
|
420
|
-
'409': { description: 'Session renderer is not ready for agent calls' },
|
|
421
|
-
},
|
|
422
|
-
},
|
|
423
|
-
},
|
|
424
|
-
'/api/windows/{sessionId}/catalog/groups': {
|
|
425
|
-
post: {
|
|
426
|
-
operationId: 'createCampaignGroup',
|
|
427
|
-
summary: 'Create a campaign group folder on Google Drive (same as UI «Создать» for group)',
|
|
428
|
-
parameters: [
|
|
429
|
-
{ name: 'sessionId', in: 'path', required: true, schema: { type: 'string' } },
|
|
430
|
-
],
|
|
431
|
-
requestBody: {
|
|
432
|
-
required: true,
|
|
433
|
-
content: {
|
|
434
|
-
'application/json': {
|
|
435
|
-
schema: {
|
|
436
|
-
type: 'object',
|
|
437
|
-
required: ['name'],
|
|
438
|
-
properties: {
|
|
439
|
-
name: { type: 'string', description: 'Group folder name under campaigns root' },
|
|
440
|
-
},
|
|
441
|
-
},
|
|
442
|
-
},
|
|
443
|
-
},
|
|
444
|
-
},
|
|
445
|
-
responses: {
|
|
446
|
-
'200': { description: 'Group folder created' },
|
|
447
|
-
'400': { description: 'Invalid payload or group already exists' },
|
|
448
|
-
'404': { description: 'Session not found' },
|
|
449
|
-
'409': { description: 'Session renderer is not ready for agent calls' },
|
|
450
|
-
},
|
|
451
|
-
},
|
|
452
|
-
},
|
|
453
|
-
'/api/windows/{sessionId}/catalog/workspaces': {
|
|
454
|
-
post: {
|
|
455
|
-
operationId: 'createWorkspaceFolder',
|
|
456
|
-
summary: 'Create a workspace folder inside a campaign group on Google Drive',
|
|
457
|
-
parameters: [
|
|
458
|
-
{ name: 'sessionId', in: 'path', required: true, schema: { type: 'string' } },
|
|
459
|
-
],
|
|
460
|
-
requestBody: {
|
|
461
|
-
required: true,
|
|
462
|
-
content: {
|
|
463
|
-
'application/json': {
|
|
464
|
-
schema: {
|
|
465
|
-
type: 'object',
|
|
466
|
-
required: ['groupName', 'name'],
|
|
467
|
-
properties: {
|
|
468
|
-
groupName: { type: 'string' },
|
|
469
|
-
name: { type: 'string', description: 'Workspace folder name' },
|
|
470
|
-
select: {
|
|
471
|
-
type: 'boolean',
|
|
472
|
-
default: true,
|
|
473
|
-
description: 'Select the new workspace in this session after creation',
|
|
474
|
-
},
|
|
475
|
-
},
|
|
476
|
-
},
|
|
477
|
-
},
|
|
478
|
-
},
|
|
479
|
-
},
|
|
480
|
-
responses: {
|
|
481
|
-
'200': { description: 'Workspace folder created' },
|
|
482
|
-
'400': { description: 'Invalid payload or folder already exists' },
|
|
483
|
-
'404': { description: 'Session not found' },
|
|
484
|
-
'409': { description: 'Session renderer is not ready for agent calls' },
|
|
485
|
-
},
|
|
486
|
-
},
|
|
487
|
-
},
|
|
488
|
-
'/api/windows/{sessionId}/generate': {
|
|
489
|
-
post: {
|
|
490
|
-
operationId: 'startGeneration',
|
|
491
|
-
summary: 'Start content, image, catalog or landing generation (same as UI buttons)',
|
|
492
|
-
parameters: [
|
|
493
|
-
{ name: 'sessionId', in: 'path', required: true, schema: { type: 'string' } },
|
|
494
|
-
],
|
|
495
|
-
requestBody: {
|
|
496
|
-
required: false,
|
|
497
|
-
content: {
|
|
498
|
-
'application/json': {
|
|
499
|
-
schema: {
|
|
500
|
-
type: 'object',
|
|
501
|
-
properties: {
|
|
502
|
-
targets: {
|
|
503
|
-
type: 'array',
|
|
504
|
-
items: {
|
|
505
|
-
type: 'string',
|
|
506
|
-
enum: ['content', 'images', 'catalog', 'landing', 'all'],
|
|
507
|
-
},
|
|
508
|
-
description: 'Sequential targets. Default: ["content"]. "all" expands to content + images.',
|
|
509
|
-
},
|
|
510
|
-
marketIds: {
|
|
511
|
-
type: 'array',
|
|
512
|
-
items: { type: 'string' },
|
|
513
|
-
description: 'Optional market ids for landing target only',
|
|
514
|
-
},
|
|
515
|
-
},
|
|
516
|
-
},
|
|
517
|
-
examples: {
|
|
518
|
-
contentAndImages: {
|
|
519
|
-
value: { targets: ['content', 'images'] },
|
|
520
|
-
},
|
|
521
|
-
fullPipeline: {
|
|
522
|
-
value: { targets: ['content', 'images', 'catalog', 'landing'] },
|
|
523
|
-
},
|
|
524
|
-
},
|
|
525
|
-
},
|
|
526
|
-
},
|
|
527
|
-
},
|
|
528
|
-
responses: {
|
|
529
|
-
'200': { description: 'Generation started or completed for requested targets' },
|
|
530
|
-
'400': { description: 'Validation failed or unknown target' },
|
|
531
|
-
'404': { description: 'Session not found' },
|
|
532
|
-
'409': { description: 'Session renderer is not ready or job already running' },
|
|
533
|
-
},
|
|
534
|
-
},
|
|
535
|
-
},
|
|
536
|
-
'/api/windows/{sessionId}/setup-campaign': {
|
|
537
|
-
post: {
|
|
538
|
-
operationId: 'setupCampaignWorkspace',
|
|
539
|
-
summary: 'Select campaign group, workspace folder, offer binding and optional generation fields',
|
|
540
|
-
parameters: [
|
|
541
|
-
{
|
|
542
|
-
name: 'sessionId',
|
|
543
|
-
in: 'path',
|
|
544
|
-
required: true,
|
|
545
|
-
schema: { type: 'string' },
|
|
546
|
-
},
|
|
547
|
-
],
|
|
548
|
-
requestBody: {
|
|
549
|
-
required: true,
|
|
550
|
-
content: {
|
|
551
|
-
'application/json': {
|
|
552
|
-
schema: { $ref: '#/components/schemas/SetupCampaignWorkspaceRequest' },
|
|
553
|
-
},
|
|
554
|
-
},
|
|
555
|
-
},
|
|
556
|
-
responses: {
|
|
557
|
-
'200': { description: 'Campaign workspace configured' },
|
|
558
|
-
'400': { description: 'Invalid payload or workspace not found' },
|
|
559
|
-
'404': { description: 'Session not found' },
|
|
560
|
-
'409': { description: 'Session renderer is not ready for agent calls' },
|
|
561
|
-
},
|
|
562
|
-
},
|
|
563
|
-
},
|
|
564
|
-
'/api/windows/{sessionId}/geo-block': {
|
|
565
|
-
post: {
|
|
566
|
-
operationId: 'fillGeoBlock',
|
|
567
|
-
summary: 'Update the geo + price + link block in one work session',
|
|
568
|
-
description: 'A single object partially updates one row and preserves omitted fields. A markets array sets the exact row list, removing rows not present in the array; omitted fields inside each row are preserved by id/index when possible.',
|
|
569
|
-
parameters: [
|
|
570
|
-
{
|
|
571
|
-
name: 'sessionId',
|
|
572
|
-
in: 'path',
|
|
573
|
-
required: true,
|
|
574
|
-
schema: { type: 'string' },
|
|
575
|
-
},
|
|
576
|
-
],
|
|
577
|
-
requestBody: {
|
|
578
|
-
required: true,
|
|
579
|
-
content: {
|
|
580
|
-
'application/json': {
|
|
581
|
-
schema: { $ref: '#/components/schemas/FillGeoBlockRequest' },
|
|
582
|
-
examples: {
|
|
583
|
-
singleMarket: {
|
|
584
|
-
value: {
|
|
585
|
-
geo: 'Romania',
|
|
586
|
-
priceWithCurrency: '149 RON',
|
|
587
|
-
link: 'https://example.com/product/',
|
|
588
|
-
},
|
|
589
|
-
},
|
|
590
|
-
multipleMarkets: {
|
|
591
|
-
value: {
|
|
592
|
-
markets: [
|
|
593
|
-
{
|
|
594
|
-
geo: 'Romania',
|
|
595
|
-
priceWithCurrency: '149 RON',
|
|
596
|
-
link: 'https://example.com/ro/',
|
|
597
|
-
},
|
|
598
|
-
{
|
|
599
|
-
geo: 'Hungary',
|
|
600
|
-
priceWithCurrency: '11400 HUF',
|
|
601
|
-
link: 'https://example.com/hu/',
|
|
602
|
-
},
|
|
603
|
-
],
|
|
604
|
-
},
|
|
605
|
-
},
|
|
606
|
-
},
|
|
607
|
-
},
|
|
608
|
-
},
|
|
609
|
-
},
|
|
610
|
-
responses: {
|
|
611
|
-
'200': {
|
|
612
|
-
description: 'Geo block was replaced',
|
|
613
|
-
content: {
|
|
614
|
-
'application/json': {
|
|
615
|
-
schema: {
|
|
616
|
-
type: 'object',
|
|
617
|
-
required: ['ok', 'markets'],
|
|
618
|
-
properties: {
|
|
619
|
-
ok: { type: 'boolean', const: true },
|
|
620
|
-
markets: {
|
|
621
|
-
type: 'array',
|
|
622
|
-
items: { $ref: '#/components/schemas/GeoBlockMarket' },
|
|
623
|
-
},
|
|
624
|
-
},
|
|
625
|
-
},
|
|
626
|
-
},
|
|
627
|
-
},
|
|
628
|
-
},
|
|
629
|
-
'400': { description: 'Invalid geo block payload' },
|
|
630
|
-
'404': { description: 'Session not found' },
|
|
631
|
-
'409': { description: 'Session renderer is not ready for agent calls' },
|
|
632
|
-
},
|
|
633
|
-
},
|
|
634
|
-
},
|
|
635
|
-
'/api/windows/{sessionId}/creative-selections': {
|
|
636
|
-
get: {
|
|
637
|
-
operationId: 'getCreativeSelections',
|
|
638
|
-
summary: 'Return selected text and image creative approaches for one work session',
|
|
639
|
-
parameters: [
|
|
640
|
-
{
|
|
641
|
-
name: 'sessionId',
|
|
642
|
-
in: 'path',
|
|
643
|
-
required: true,
|
|
644
|
-
schema: { type: 'string' },
|
|
645
|
-
},
|
|
646
|
-
],
|
|
647
|
-
responses: {
|
|
648
|
-
'200': {
|
|
649
|
-
description: 'Current creative approach selections',
|
|
650
|
-
content: {
|
|
651
|
-
'application/json': {
|
|
652
|
-
schema: {
|
|
653
|
-
type: 'object',
|
|
654
|
-
required: ['ok', 'selections'],
|
|
655
|
-
properties: {
|
|
656
|
-
ok: { type: 'boolean', const: true },
|
|
657
|
-
selections: { $ref: '#/components/schemas/CreativeSelections' },
|
|
658
|
-
},
|
|
659
|
-
},
|
|
660
|
-
},
|
|
661
|
-
},
|
|
662
|
-
},
|
|
663
|
-
'404': { description: 'Session not found' },
|
|
664
|
-
'409': { description: 'Session renderer is not ready for agent calls' },
|
|
665
|
-
},
|
|
666
|
-
},
|
|
667
|
-
post: {
|
|
668
|
-
operationId: 'setCreativeSelections',
|
|
669
|
-
summary: 'Set selected text and image creative approaches for one work session',
|
|
670
|
-
parameters: [
|
|
671
|
-
{
|
|
672
|
-
name: 'sessionId',
|
|
673
|
-
in: 'path',
|
|
674
|
-
required: true,
|
|
675
|
-
schema: { type: 'string' },
|
|
676
|
-
},
|
|
677
|
-
],
|
|
678
|
-
requestBody: {
|
|
679
|
-
required: true,
|
|
680
|
-
content: {
|
|
681
|
-
'application/json': {
|
|
682
|
-
schema: { $ref: '#/components/schemas/SetCreativeSelectionsRequest' },
|
|
683
|
-
examples: {
|
|
684
|
-
uiNumbers: {
|
|
685
|
-
value: {
|
|
686
|
-
selectedTextApproachNumbers: [1, 2, 3],
|
|
687
|
-
imageApproachCounts: [1, 1, 0, 0, 2, 0, 0, 0, 0, 0],
|
|
688
|
-
},
|
|
689
|
-
},
|
|
690
|
-
imageNumbers: {
|
|
691
|
-
value: {
|
|
692
|
-
selectedTextApproachIndices: [0, 1, 2],
|
|
693
|
-
selectedImageApproachNumbers: [1, 5],
|
|
694
|
-
},
|
|
695
|
-
},
|
|
696
|
-
},
|
|
697
|
-
},
|
|
698
|
-
},
|
|
699
|
-
},
|
|
700
|
-
responses: {
|
|
701
|
-
'200': {
|
|
702
|
-
description: 'Creative approach selections were updated',
|
|
703
|
-
content: {
|
|
704
|
-
'application/json': {
|
|
705
|
-
schema: {
|
|
706
|
-
type: 'object',
|
|
707
|
-
required: ['ok', 'selections'],
|
|
708
|
-
properties: {
|
|
709
|
-
ok: { type: 'boolean', const: true },
|
|
710
|
-
selections: { $ref: '#/components/schemas/CreativeSelections' },
|
|
711
|
-
},
|
|
712
|
-
},
|
|
713
|
-
},
|
|
714
|
-
},
|
|
715
|
-
},
|
|
716
|
-
'400': { description: 'Invalid creative selections payload' },
|
|
717
|
-
'404': { description: 'Session not found' },
|
|
718
|
-
'409': { description: 'Session renderer is not ready for agent calls' },
|
|
719
|
-
},
|
|
720
|
-
},
|
|
721
|
-
},
|
|
722
|
-
'/api/windows/{sessionId}/capabilities': {
|
|
723
|
-
get: {
|
|
724
|
-
operationId: 'getCapabilities',
|
|
725
|
-
summary: 'Return what the agent is allowed to read or change in this session',
|
|
726
|
-
responses: { '200': { description: 'Session capabilities' } },
|
|
727
|
-
},
|
|
728
|
-
},
|
|
729
|
-
'/api/windows/{sessionId}/validation': {
|
|
730
|
-
get: {
|
|
731
|
-
operationId: 'getValidation',
|
|
732
|
-
summary: 'Return readiness checks and blocking issues for this session',
|
|
733
|
-
responses: { '200': { description: 'Validation checklist' } },
|
|
734
|
-
},
|
|
735
|
-
},
|
|
736
|
-
'/api/windows/{sessionId}/jobs': {
|
|
737
|
-
get: {
|
|
738
|
-
operationId: 'listJobs',
|
|
739
|
-
summary: 'Return observable UI job statuses for this session',
|
|
740
|
-
responses: { '200': { description: 'Known job statuses' } },
|
|
741
|
-
},
|
|
742
|
-
},
|
|
743
|
-
'/api/windows/{sessionId}/jobs/{jobId}': {
|
|
744
|
-
get: {
|
|
745
|
-
operationId: 'getJobStatus',
|
|
746
|
-
summary: 'Return one observable UI job status',
|
|
747
|
-
parameters: [
|
|
748
|
-
{ name: 'sessionId', in: 'path', required: true, schema: { type: 'string' } },
|
|
749
|
-
{ name: 'jobId', in: 'path', required: true, schema: { type: 'string' } },
|
|
750
|
-
],
|
|
751
|
-
responses: { '200': { description: 'Job status' }, '404': { description: 'Unknown job id' } },
|
|
752
|
-
},
|
|
753
|
-
},
|
|
754
|
-
'/api/windows/{sessionId}/generated': {
|
|
755
|
-
get: {
|
|
756
|
-
operationId: 'getGenerated',
|
|
757
|
-
summary: 'Return generated titles, texts, images, catalog rows and landing artifacts',
|
|
758
|
-
responses: { '200': { description: 'Generated session artifacts' } },
|
|
759
|
-
},
|
|
760
|
-
patch: {
|
|
761
|
-
operationId: 'patchGenerated',
|
|
762
|
-
summary: 'Partially edit generated titles, texts or image metadata without starting generation',
|
|
763
|
-
requestBody: {
|
|
764
|
-
required: true,
|
|
765
|
-
content: {
|
|
766
|
-
'application/json': {
|
|
767
|
-
schema: { $ref: '#/components/schemas/PatchGeneratedRequest' },
|
|
768
|
-
examples: {
|
|
769
|
-
titleText: {
|
|
770
|
-
value: {
|
|
771
|
-
titles: [{ index: 1, title: 'New title' }],
|
|
772
|
-
texts: [{ index: 1, text: 'New text' }],
|
|
773
|
-
},
|
|
774
|
-
},
|
|
775
|
-
imageMetadata: {
|
|
776
|
-
value: {
|
|
777
|
-
images: [{ index: 1, failed: false, errorMessage: '' }],
|
|
778
|
-
},
|
|
779
|
-
},
|
|
780
|
-
},
|
|
781
|
-
},
|
|
782
|
-
},
|
|
783
|
-
},
|
|
784
|
-
responses: { '200': { description: 'Generated artifacts were updated' }, '400': { description: 'Invalid patch' } },
|
|
785
|
-
},
|
|
786
|
-
},
|
|
787
|
-
'/api/windows/{sessionId}/logs': {
|
|
788
|
-
get: {
|
|
789
|
-
operationId: 'getLogs',
|
|
790
|
-
summary: 'Return recent content, image, landing and catalog replacement logs',
|
|
791
|
-
responses: { '200': { description: 'Session logs' } },
|
|
792
|
-
},
|
|
793
|
-
},
|
|
794
|
-
'/api/spec': {
|
|
795
|
-
get: {
|
|
796
|
-
operationId: 'getAgentApiSpec',
|
|
797
|
-
summary: 'Return the machine-readable Agent API description',
|
|
798
|
-
responses: {
|
|
799
|
-
'200': {
|
|
800
|
-
description: 'OpenAPI-like API description for automation agents',
|
|
801
|
-
},
|
|
802
|
-
},
|
|
803
|
-
},
|
|
804
|
-
},
|
|
805
|
-
},
|
|
806
|
-
components: {
|
|
807
|
-
schemas: {
|
|
808
|
-
AgentWorkSessionInfo: {
|
|
809
|
-
type: 'object',
|
|
810
|
-
required: ['id', 'title', 'description', 'active', 'url', 'endpoints'],
|
|
811
|
-
properties: {
|
|
812
|
-
id: {
|
|
813
|
-
type: 'string',
|
|
814
|
-
description: 'Stable workspace/session id to use in future agent operations.',
|
|
815
|
-
},
|
|
816
|
-
title: { type: 'string' },
|
|
817
|
-
description: {
|
|
818
|
-
type: 'string',
|
|
819
|
-
description: 'Short human-readable summary of what is open in this session.',
|
|
820
|
-
},
|
|
821
|
-
folderId: {
|
|
822
|
-
type: 'string',
|
|
823
|
-
description: 'Current Google Drive folder id, when selected.',
|
|
824
|
-
},
|
|
825
|
-
folderPath: {
|
|
826
|
-
type: 'string',
|
|
827
|
-
description: 'Two-level display path for the current Drive folder, for example OFFERS/OfferName.',
|
|
828
|
-
},
|
|
829
|
-
active: { type: 'boolean' },
|
|
830
|
-
url: {
|
|
831
|
-
type: 'string',
|
|
832
|
-
description: 'Full local URL for reading this session state.',
|
|
833
|
-
},
|
|
834
|
-
endpoints: {
|
|
835
|
-
type: 'object',
|
|
836
|
-
required: ['info', 'update', 'geoBlock', 'creativeSelections', 'capabilities', 'validation', 'generated', 'logs', 'jobs'],
|
|
837
|
-
properties: {
|
|
838
|
-
info: { type: 'string' },
|
|
839
|
-
update: { type: 'string' },
|
|
840
|
-
geoBlock: { type: 'string' },
|
|
841
|
-
creativeSelections: { type: 'string' },
|
|
842
|
-
capabilities: { type: 'string' },
|
|
843
|
-
validation: { type: 'string' },
|
|
844
|
-
generated: { type: 'string' },
|
|
845
|
-
logs: { type: 'string' },
|
|
846
|
-
jobs: { type: 'string' },
|
|
847
|
-
},
|
|
848
|
-
},
|
|
849
|
-
},
|
|
850
|
-
},
|
|
851
|
-
GeoBlockMarket: {
|
|
852
|
-
type: 'object',
|
|
853
|
-
properties: {
|
|
854
|
-
id: { type: 'string' },
|
|
855
|
-
geo: { type: 'string' },
|
|
856
|
-
priceWithCurrency: { type: 'string' },
|
|
857
|
-
link: { type: 'string' },
|
|
858
|
-
},
|
|
859
|
-
},
|
|
860
|
-
FillGeoBlockRequest: {
|
|
861
|
-
oneOf: [
|
|
862
|
-
{ $ref: '#/components/schemas/GeoBlockMarket' },
|
|
863
|
-
{
|
|
864
|
-
type: 'object',
|
|
865
|
-
required: ['markets'],
|
|
866
|
-
properties: {
|
|
867
|
-
markets: {
|
|
868
|
-
type: 'array',
|
|
869
|
-
minItems: 1,
|
|
870
|
-
items: { $ref: '#/components/schemas/GeoBlockMarket' },
|
|
871
|
-
},
|
|
872
|
-
},
|
|
873
|
-
},
|
|
874
|
-
],
|
|
875
|
-
},
|
|
876
|
-
PatchSessionInfoRequest: {
|
|
877
|
-
type: 'object',
|
|
878
|
-
description: 'Partial session update. Omitted fields are left unchanged.',
|
|
879
|
-
properties: {
|
|
880
|
-
generateProduct: { type: 'string' },
|
|
881
|
-
generateAdditionalInfo: { type: 'string' },
|
|
882
|
-
generateGeo: { type: 'string' },
|
|
883
|
-
generatePriceWithCurrency: { type: 'string' },
|
|
884
|
-
link: { type: 'string' },
|
|
885
|
-
generationMarkets: {
|
|
886
|
-
type: 'array',
|
|
887
|
-
items: { $ref: '#/components/schemas/GeoBlockMarket' },
|
|
888
|
-
},
|
|
889
|
-
markets: {
|
|
890
|
-
type: 'array',
|
|
891
|
-
items: { $ref: '#/components/schemas/GeoBlockMarket' },
|
|
892
|
-
},
|
|
893
|
-
geoBlock: {
|
|
894
|
-
type: 'array',
|
|
895
|
-
items: { $ref: '#/components/schemas/GeoBlockMarket' },
|
|
896
|
-
},
|
|
897
|
-
generation: {
|
|
898
|
-
type: 'object',
|
|
899
|
-
properties: {
|
|
900
|
-
product: { type: 'string' },
|
|
901
|
-
additionalInfo: { type: 'string' },
|
|
902
|
-
firstGeo: { type: 'string' },
|
|
903
|
-
firstPriceWithCurrency: { type: 'string' },
|
|
904
|
-
firstLink: { type: 'string' },
|
|
905
|
-
geoBlock: {
|
|
906
|
-
type: 'array',
|
|
907
|
-
items: { $ref: '#/components/schemas/GeoBlockMarket' },
|
|
908
|
-
},
|
|
909
|
-
},
|
|
910
|
-
},
|
|
911
|
-
catalogUrlSettings: { type: 'object' },
|
|
912
|
-
creativeSelections: { $ref: '#/components/schemas/SetCreativeSelectionsRequest' },
|
|
913
|
-
promptSettings: { type: 'object' },
|
|
914
|
-
drive: {
|
|
915
|
-
type: 'object',
|
|
916
|
-
description: 'Select campaign group, workspace folder and offer binding before generation.',
|
|
917
|
-
properties: {
|
|
918
|
-
groupName: { type: 'string' },
|
|
919
|
-
workspaceFolderId: { type: 'string' },
|
|
920
|
-
workspaceName: { type: 'string' },
|
|
921
|
-
offer: { type: 'string' },
|
|
922
|
-
},
|
|
923
|
-
},
|
|
924
|
-
},
|
|
925
|
-
},
|
|
926
|
-
SetupCampaignWorkspaceRequest: {
|
|
927
|
-
type: 'object',
|
|
928
|
-
required: ['groupName'],
|
|
929
|
-
properties: {
|
|
930
|
-
groupName: { type: 'string' },
|
|
931
|
-
workspaceFolderId: { type: 'string' },
|
|
932
|
-
workspaceName: { type: 'string' },
|
|
933
|
-
offer: { type: 'string' },
|
|
934
|
-
generation: { type: 'object' },
|
|
935
|
-
creativeSelections: { $ref: '#/components/schemas/SetCreativeSelectionsRequest' },
|
|
936
|
-
},
|
|
937
|
-
},
|
|
938
|
-
DriveCatalog: {
|
|
939
|
-
type: 'object',
|
|
940
|
-
description: 'Offers root, campaigns tree and current session Drive bindings.',
|
|
941
|
-
},
|
|
942
|
-
CreativeSelections: {
|
|
943
|
-
type: 'object',
|
|
944
|
-
required: ['textApproaches', 'imageApproaches'],
|
|
945
|
-
properties: {
|
|
946
|
-
textApproaches: {
|
|
947
|
-
type: 'object',
|
|
948
|
-
required: ['selectedIndices', 'selectedNumbers'],
|
|
949
|
-
properties: {
|
|
950
|
-
selectedIndices: {
|
|
951
|
-
type: 'array',
|
|
952
|
-
description: '0-based selected text approach indices.',
|
|
953
|
-
items: { type: 'number' },
|
|
954
|
-
},
|
|
955
|
-
selectedNumbers: {
|
|
956
|
-
type: 'array',
|
|
957
|
-
description: '1-based selected text approach numbers as shown in the UI.',
|
|
958
|
-
items: { type: 'number' },
|
|
959
|
-
},
|
|
960
|
-
},
|
|
961
|
-
},
|
|
962
|
-
imageApproaches: {
|
|
963
|
-
type: 'object',
|
|
964
|
-
required: ['counts', 'selectedIndices', 'selectedNumbers', 'maxPerApproach'],
|
|
965
|
-
properties: {
|
|
966
|
-
counts: {
|
|
967
|
-
type: 'array',
|
|
968
|
-
description: 'Image count per creative approach, 10 items, values from 0 to maxPerApproach.',
|
|
969
|
-
items: { type: 'number' },
|
|
970
|
-
},
|
|
971
|
-
selectedIndices: {
|
|
972
|
-
type: 'array',
|
|
973
|
-
description: '0-based image approach indices where count is greater than 0.',
|
|
974
|
-
items: { type: 'number' },
|
|
975
|
-
},
|
|
976
|
-
selectedNumbers: {
|
|
977
|
-
type: 'array',
|
|
978
|
-
description: '1-based image approach numbers where count is greater than 0.',
|
|
979
|
-
items: { type: 'number' },
|
|
980
|
-
},
|
|
981
|
-
maxPerApproach: { type: 'number' },
|
|
982
|
-
},
|
|
983
|
-
},
|
|
984
|
-
},
|
|
985
|
-
},
|
|
986
|
-
SetCreativeSelectionsRequest: {
|
|
987
|
-
type: 'object',
|
|
988
|
-
properties: {
|
|
989
|
-
selectedTextApproachIndices: {
|
|
990
|
-
type: 'array',
|
|
991
|
-
description: '0-based text approach indices. At least two unique approaches are required if provided.',
|
|
992
|
-
items: { type: 'number' },
|
|
993
|
-
},
|
|
994
|
-
selectedTextApproachNumbers: {
|
|
995
|
-
type: 'array',
|
|
996
|
-
description: '1-based text approach numbers as shown in the UI. At least two unique approaches are required if provided.',
|
|
997
|
-
items: { type: 'number' },
|
|
998
|
-
},
|
|
999
|
-
imageApproachCounts: {
|
|
1000
|
-
type: 'array',
|
|
1001
|
-
description: 'Image count per creative approach, 10 items, values from 0 to maxPerApproach.',
|
|
1002
|
-
items: { type: 'number' },
|
|
1003
|
-
},
|
|
1004
|
-
selectedImageApproachIndices: {
|
|
1005
|
-
type: 'array',
|
|
1006
|
-
description: '0-based image approach indices. Each selected approach gets count=1.',
|
|
1007
|
-
items: { type: 'number' },
|
|
1008
|
-
},
|
|
1009
|
-
selectedImageApproachNumbers: {
|
|
1010
|
-
type: 'array',
|
|
1011
|
-
description: '1-based image approach numbers as shown in the UI. Each selected approach gets count=1.',
|
|
1012
|
-
items: { type: 'number' },
|
|
1013
|
-
},
|
|
1014
|
-
},
|
|
1015
|
-
},
|
|
1016
|
-
PatchGeneratedRequest: {
|
|
1017
|
-
type: 'object',
|
|
1018
|
-
description: 'Partial edit for generated artifacts. Omitted fields are unchanged.',
|
|
1019
|
-
properties: {
|
|
1020
|
-
titles: {
|
|
1021
|
-
type: 'array',
|
|
1022
|
-
items: {
|
|
1023
|
-
type: 'object',
|
|
1024
|
-
required: ['index'],
|
|
1025
|
-
properties: {
|
|
1026
|
-
index: { type: 'number', description: '1-based title row index.' },
|
|
1027
|
-
title: { type: 'string' },
|
|
1028
|
-
failed: { type: 'boolean' },
|
|
1029
|
-
errorMessage: { type: 'string' },
|
|
1030
|
-
},
|
|
1031
|
-
},
|
|
1032
|
-
},
|
|
1033
|
-
texts: {
|
|
1034
|
-
type: 'array',
|
|
1035
|
-
items: {
|
|
1036
|
-
type: 'object',
|
|
1037
|
-
required: ['index'],
|
|
1038
|
-
properties: {
|
|
1039
|
-
index: { type: 'number', description: '1-based text row index.' },
|
|
1040
|
-
text: { type: 'string' },
|
|
1041
|
-
failed: { type: 'boolean' },
|
|
1042
|
-
errorMessage: { type: 'string' },
|
|
1043
|
-
},
|
|
1044
|
-
},
|
|
1045
|
-
},
|
|
1046
|
-
images: {
|
|
1047
|
-
type: 'array',
|
|
1048
|
-
items: {
|
|
1049
|
-
type: 'object',
|
|
1050
|
-
required: ['index'],
|
|
1051
|
-
properties: {
|
|
1052
|
-
index: { type: 'number', description: '1-based image slot index.' },
|
|
1053
|
-
imageUrl: { type: 'string' },
|
|
1054
|
-
approach: { type: 'string' },
|
|
1055
|
-
geo: { type: 'string' },
|
|
1056
|
-
priceWithCurrency: { type: 'string' },
|
|
1057
|
-
aspectRatio: { type: 'string' },
|
|
1058
|
-
uploaded: { type: 'boolean' },
|
|
1059
|
-
failed: { type: 'boolean' },
|
|
1060
|
-
errorMessage: { type: 'string' },
|
|
1061
|
-
checkStatus: { type: 'string' },
|
|
1062
|
-
checkResult: { type: 'string' },
|
|
1063
|
-
checkErrors: { type: 'array', items: { type: 'string' } },
|
|
1064
|
-
},
|
|
1065
|
-
},
|
|
1066
|
-
},
|
|
1067
|
-
},
|
|
1068
|
-
},
|
|
1069
|
-
AgentSessionInfo: {
|
|
1070
|
-
type: 'object',
|
|
1071
|
-
description: 'Live renderer state for one Docs Combiner work session.',
|
|
1072
|
-
required: ['id', 'title', 'updatedAt', 'generation', 'snapshot'],
|
|
1073
|
-
properties: {
|
|
1074
|
-
id: { type: 'string' },
|
|
1075
|
-
title: { type: 'string' },
|
|
1076
|
-
updatedAt: { type: 'string', format: 'date-time' },
|
|
1077
|
-
drive: { type: 'object' },
|
|
1078
|
-
generation: {
|
|
1079
|
-
type: 'object',
|
|
1080
|
-
properties: {
|
|
1081
|
-
product: { type: 'string' },
|
|
1082
|
-
additionalInfo: { type: 'string' },
|
|
1083
|
-
geoBlock: {
|
|
1084
|
-
type: 'array',
|
|
1085
|
-
items: { $ref: '#/components/schemas/GeoBlockMarket' },
|
|
1086
|
-
},
|
|
1087
|
-
firstGeo: { type: 'string' },
|
|
1088
|
-
firstPriceWithCurrency: { type: 'string' },
|
|
1089
|
-
firstLink: { type: 'string' },
|
|
1090
|
-
brand: { type: 'string' },
|
|
1091
|
-
},
|
|
1092
|
-
},
|
|
1093
|
-
catalogUrlSettings: { type: 'object' },
|
|
1094
|
-
promptSettings: { type: 'object' },
|
|
1095
|
-
snapshot: {
|
|
1096
|
-
type: 'object',
|
|
1097
|
-
description: 'Serializable workspace snapshot used by the app autosave.',
|
|
1098
|
-
},
|
|
1099
|
-
},
|
|
1100
|
-
},
|
|
1101
|
-
},
|
|
1102
|
-
},
|
|
1103
|
-
};
|
|
1104
|
-
}
|
|
1105
417
|
function startAgentApiServer() {
|
|
1106
418
|
if (agentApiServer)
|
|
1107
419
|
return;
|
|
1108
420
|
const api = (0, express_1.default)();
|
|
1109
421
|
api.disable('x-powered-by');
|
|
1110
422
|
api.use(express_1.default.json({ limit: '1mb' }));
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
ok: true,
|
|
1129
|
-
ready: true,
|
|
1130
|
-
session,
|
|
1131
|
-
url: sessionUrl,
|
|
1132
|
-
endpoints: (_c = getAgentWorkSessions().find(item => item.id === workspaceId)) === null || _c === void 0 ? void 0 : _c.endpoints,
|
|
1133
|
-
});
|
|
1134
|
-
return;
|
|
1135
|
-
}
|
|
1136
|
-
res.status(202).json({
|
|
1137
|
-
ok: true,
|
|
1138
|
-
ready: false,
|
|
1139
|
-
session: { id: workspaceId, title: 'Новый оффер' },
|
|
1140
|
-
url: sessionUrl,
|
|
1141
|
-
message: 'Window opened, but renderer Agent API is not ready yet. Retry GET /api/windows/{sessionId}.',
|
|
1142
|
-
});
|
|
1143
|
-
}));
|
|
1144
|
-
api.get('/api/catalog', (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
1145
|
-
var _a;
|
|
1146
|
-
const preferredSessionId = String((_a = req.query.sessionId) !== null && _a !== void 0 ? _a : '').trim();
|
|
1147
|
-
const response = yield callFirstReadyAgentRendererApi('getDriveCatalog', null, preferredSessionId || undefined);
|
|
1148
|
-
if (!response.ok) {
|
|
1149
|
-
res.status(response.status).json({ ok: false, error: response.error });
|
|
1150
|
-
return;
|
|
1151
|
-
}
|
|
1152
|
-
res.json({ ok: true, catalog: response.result });
|
|
1153
|
-
}));
|
|
1154
|
-
api.get('/api/windows/:sessionId', (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
1155
|
-
var _a;
|
|
1156
|
-
const sessionId = String((_a = req.params.sessionId) !== null && _a !== void 0 ? _a : '').trim();
|
|
1157
|
-
if (!sessionId) {
|
|
1158
|
-
res.status(400).json({ ok: false, error: 'Session id is required.' });
|
|
1159
|
-
return;
|
|
1160
|
-
}
|
|
1161
|
-
const response = yield callAgentRendererApi(sessionId, 'getSessionInfo');
|
|
1162
|
-
if (!response.ok) {
|
|
1163
|
-
res.status(response.status).json({ ok: false, error: response.error });
|
|
1164
|
-
return;
|
|
1165
|
-
}
|
|
1166
|
-
res.json({ ok: true, session: response.result });
|
|
1167
|
-
}));
|
|
1168
|
-
api.patch('/api/windows/:sessionId', (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
1169
|
-
var _a, _b;
|
|
1170
|
-
const sessionId = String((_a = req.params.sessionId) !== null && _a !== void 0 ? _a : '').trim();
|
|
1171
|
-
if (!sessionId) {
|
|
1172
|
-
res.status(400).json({ ok: false, error: 'Session id is required.' });
|
|
1173
|
-
return;
|
|
1174
|
-
}
|
|
1175
|
-
const response = yield callAgentRendererApi(sessionId, 'patchSessionInfo', req.body);
|
|
1176
|
-
if (!response.ok) {
|
|
1177
|
-
res.status(response.status).json({ ok: false, error: response.error });
|
|
1178
|
-
return;
|
|
1179
|
-
}
|
|
1180
|
-
if (((_b = response.result) === null || _b === void 0 ? void 0 : _b.ok) === false) {
|
|
1181
|
-
res.status(400).json(response.result);
|
|
1182
|
-
return;
|
|
1183
|
-
}
|
|
1184
|
-
res.json(response.result);
|
|
1185
|
-
}));
|
|
1186
|
-
api.post('/api/windows/:sessionId/setup-campaign', (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
1187
|
-
var _a, _b;
|
|
1188
|
-
const sessionId = String((_a = req.params.sessionId) !== null && _a !== void 0 ? _a : '').trim();
|
|
1189
|
-
if (!sessionId) {
|
|
1190
|
-
res.status(400).json({ ok: false, error: 'Session id is required.' });
|
|
1191
|
-
return;
|
|
1192
|
-
}
|
|
1193
|
-
const response = yield callAgentRendererApi(sessionId, 'setupCampaignWorkspace', req.body);
|
|
1194
|
-
if (!response.ok) {
|
|
1195
|
-
res.status(response.status).json({ ok: false, error: response.error });
|
|
1196
|
-
return;
|
|
1197
|
-
}
|
|
1198
|
-
if (((_b = response.result) === null || _b === void 0 ? void 0 : _b.ok) === false) {
|
|
1199
|
-
res.status(400).json(response.result);
|
|
1200
|
-
return;
|
|
1201
|
-
}
|
|
1202
|
-
res.json(response.result);
|
|
1203
|
-
}));
|
|
1204
|
-
api.post('/api/windows/:sessionId/catalog/groups', (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
1205
|
-
var _a, _b;
|
|
1206
|
-
const sessionId = String((_a = req.params.sessionId) !== null && _a !== void 0 ? _a : '').trim();
|
|
1207
|
-
if (!sessionId) {
|
|
1208
|
-
res.status(400).json({ ok: false, error: 'Session id is required.' });
|
|
1209
|
-
return;
|
|
1210
|
-
}
|
|
1211
|
-
const response = yield callAgentRendererApi(sessionId, 'createCampaignGroup', req.body);
|
|
1212
|
-
if (!response.ok) {
|
|
1213
|
-
res.status(response.status).json({ ok: false, error: response.error });
|
|
1214
|
-
return;
|
|
1215
|
-
}
|
|
1216
|
-
if (((_b = response.result) === null || _b === void 0 ? void 0 : _b.ok) === false) {
|
|
1217
|
-
res.status(400).json(response.result);
|
|
1218
|
-
return;
|
|
1219
|
-
}
|
|
1220
|
-
res.json(response.result);
|
|
1221
|
-
}));
|
|
1222
|
-
api.post('/api/windows/:sessionId/catalog/workspaces', (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
1223
|
-
var _a, _b;
|
|
1224
|
-
const sessionId = String((_a = req.params.sessionId) !== null && _a !== void 0 ? _a : '').trim();
|
|
1225
|
-
if (!sessionId) {
|
|
1226
|
-
res.status(400).json({ ok: false, error: 'Session id is required.' });
|
|
1227
|
-
return;
|
|
1228
|
-
}
|
|
1229
|
-
const response = yield callAgentRendererApi(sessionId, 'createWorkspaceFolder', req.body);
|
|
1230
|
-
if (!response.ok) {
|
|
1231
|
-
res.status(response.status).json({ ok: false, error: response.error });
|
|
1232
|
-
return;
|
|
1233
|
-
}
|
|
1234
|
-
if (((_b = response.result) === null || _b === void 0 ? void 0 : _b.ok) === false) {
|
|
1235
|
-
res.status(400).json(response.result);
|
|
1236
|
-
return;
|
|
1237
|
-
}
|
|
1238
|
-
res.json(response.result);
|
|
1239
|
-
}));
|
|
1240
|
-
api.post('/api/windows/:sessionId/generate', (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
1241
|
-
var _a, _b;
|
|
1242
|
-
const sessionId = String((_a = req.params.sessionId) !== null && _a !== void 0 ? _a : '').trim();
|
|
1243
|
-
if (!sessionId) {
|
|
1244
|
-
res.status(400).json({ ok: false, error: 'Session id is required.' });
|
|
1245
|
-
return;
|
|
1246
|
-
}
|
|
1247
|
-
const body = req.body && typeof req.body === 'object' ? req.body : {};
|
|
1248
|
-
const response = yield callAgentRendererApi(sessionId, 'startGeneration', body);
|
|
1249
|
-
if (!response.ok) {
|
|
1250
|
-
res.status(response.status).json({ ok: false, error: response.error });
|
|
1251
|
-
return;
|
|
1252
|
-
}
|
|
1253
|
-
if (((_b = response.result) === null || _b === void 0 ? void 0 : _b.ok) === false) {
|
|
1254
|
-
res.status(400).json(response.result);
|
|
1255
|
-
return;
|
|
1256
|
-
}
|
|
1257
|
-
res.json(response.result);
|
|
1258
|
-
}));
|
|
1259
|
-
api.post('/api/windows/:sessionId/geo-block', (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
1260
|
-
var _a, _b;
|
|
1261
|
-
const sessionId = String((_a = req.params.sessionId) !== null && _a !== void 0 ? _a : '').trim();
|
|
1262
|
-
if (!sessionId) {
|
|
1263
|
-
res.status(400).json({ ok: false, error: 'Session id is required.' });
|
|
1264
|
-
return;
|
|
1265
|
-
}
|
|
1266
|
-
const response = yield callAgentRendererApi(sessionId, 'fillGeoBlock', req.body);
|
|
1267
|
-
if (!response.ok) {
|
|
1268
|
-
res.status(response.status).json({ ok: false, error: response.error });
|
|
1269
|
-
return;
|
|
1270
|
-
}
|
|
1271
|
-
if (((_b = response.result) === null || _b === void 0 ? void 0 : _b.ok) === false) {
|
|
1272
|
-
res.status(400).json(response.result);
|
|
1273
|
-
return;
|
|
1274
|
-
}
|
|
1275
|
-
res.json(response.result);
|
|
1276
|
-
}));
|
|
1277
|
-
api.get('/api/windows/:sessionId/creative-selections', (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
1278
|
-
var _a;
|
|
1279
|
-
const sessionId = String((_a = req.params.sessionId) !== null && _a !== void 0 ? _a : '').trim();
|
|
1280
|
-
if (!sessionId) {
|
|
1281
|
-
res.status(400).json({ ok: false, error: 'Session id is required.' });
|
|
1282
|
-
return;
|
|
1283
|
-
}
|
|
1284
|
-
const response = yield callAgentRendererApi(sessionId, 'getCreativeSelections');
|
|
1285
|
-
if (!response.ok) {
|
|
1286
|
-
res.status(response.status).json({ ok: false, error: response.error });
|
|
1287
|
-
return;
|
|
1288
|
-
}
|
|
1289
|
-
res.json({ ok: true, selections: response.result });
|
|
1290
|
-
}));
|
|
1291
|
-
api.post('/api/windows/:sessionId/creative-selections', (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
1292
|
-
var _a, _b;
|
|
1293
|
-
const sessionId = String((_a = req.params.sessionId) !== null && _a !== void 0 ? _a : '').trim();
|
|
1294
|
-
if (!sessionId) {
|
|
1295
|
-
res.status(400).json({ ok: false, error: 'Session id is required.' });
|
|
1296
|
-
return;
|
|
1297
|
-
}
|
|
1298
|
-
const response = yield callAgentRendererApi(sessionId, 'setCreativeSelections', req.body);
|
|
1299
|
-
if (!response.ok) {
|
|
1300
|
-
res.status(response.status).json({ ok: false, error: response.error });
|
|
1301
|
-
return;
|
|
1302
|
-
}
|
|
1303
|
-
if (((_b = response.result) === null || _b === void 0 ? void 0 : _b.ok) === false) {
|
|
1304
|
-
res.status(400).json(response.result);
|
|
1305
|
-
return;
|
|
1306
|
-
}
|
|
1307
|
-
res.json(response.result);
|
|
1308
|
-
}));
|
|
1309
|
-
api.get('/api/windows/:sessionId/capabilities', (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
1310
|
-
var _a;
|
|
1311
|
-
const sessionId = String((_a = req.params.sessionId) !== null && _a !== void 0 ? _a : '').trim();
|
|
1312
|
-
const response = yield callAgentRendererApi(sessionId, 'getCapabilities');
|
|
1313
|
-
if (!response.ok) {
|
|
1314
|
-
res.status(response.status).json({ ok: false, error: response.error });
|
|
1315
|
-
return;
|
|
1316
|
-
}
|
|
1317
|
-
res.json({ ok: true, capabilities: response.result });
|
|
1318
|
-
}));
|
|
1319
|
-
api.get('/api/windows/:sessionId/validation', (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
1320
|
-
var _a;
|
|
1321
|
-
const sessionId = String((_a = req.params.sessionId) !== null && _a !== void 0 ? _a : '').trim();
|
|
1322
|
-
const response = yield callAgentRendererApi(sessionId, 'getValidation');
|
|
1323
|
-
if (!response.ok) {
|
|
1324
|
-
res.status(response.status).json({ ok: false, error: response.error });
|
|
1325
|
-
return;
|
|
1326
|
-
}
|
|
1327
|
-
res.json({ ok: true, validation: response.result });
|
|
1328
|
-
}));
|
|
1329
|
-
api.get('/api/windows/:sessionId/jobs', (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
1330
|
-
var _a;
|
|
1331
|
-
const sessionId = String((_a = req.params.sessionId) !== null && _a !== void 0 ? _a : '').trim();
|
|
1332
|
-
const response = yield callAgentRendererApi(sessionId, 'getJobStatus', {});
|
|
1333
|
-
if (!response.ok) {
|
|
1334
|
-
res.status(response.status).json({ ok: false, error: response.error });
|
|
1335
|
-
return;
|
|
1336
|
-
}
|
|
1337
|
-
res.json(response.result);
|
|
1338
|
-
}));
|
|
1339
|
-
api.get('/api/windows/:sessionId/jobs/:jobId', (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
1340
|
-
var _a, _b;
|
|
1341
|
-
const sessionId = String((_a = req.params.sessionId) !== null && _a !== void 0 ? _a : '').trim();
|
|
1342
|
-
const response = yield callAgentRendererApi(sessionId, 'getJobStatus', { jobId: req.params.jobId });
|
|
1343
|
-
if (!response.ok) {
|
|
1344
|
-
res.status(response.status).json({ ok: false, error: response.error });
|
|
1345
|
-
return;
|
|
1346
|
-
}
|
|
1347
|
-
if (((_b = response.result) === null || _b === void 0 ? void 0 : _b.ok) === false) {
|
|
1348
|
-
res.status(404).json(response.result);
|
|
1349
|
-
return;
|
|
1350
|
-
}
|
|
1351
|
-
res.json(response.result);
|
|
1352
|
-
}));
|
|
1353
|
-
api.get('/api/windows/:sessionId/generated', (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
1354
|
-
var _a;
|
|
1355
|
-
const sessionId = String((_a = req.params.sessionId) !== null && _a !== void 0 ? _a : '').trim();
|
|
1356
|
-
const response = yield callAgentRendererApi(sessionId, 'getGenerated');
|
|
1357
|
-
if (!response.ok) {
|
|
1358
|
-
res.status(response.status).json({ ok: false, error: response.error });
|
|
1359
|
-
return;
|
|
1360
|
-
}
|
|
1361
|
-
res.json({ ok: true, generated: response.result });
|
|
1362
|
-
}));
|
|
1363
|
-
api.patch('/api/windows/:sessionId/generated', (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
1364
|
-
var _a, _b;
|
|
1365
|
-
const sessionId = String((_a = req.params.sessionId) !== null && _a !== void 0 ? _a : '').trim();
|
|
1366
|
-
const response = yield callAgentRendererApi(sessionId, 'patchGenerated', req.body);
|
|
1367
|
-
if (!response.ok) {
|
|
1368
|
-
res.status(response.status).json({ ok: false, error: response.error });
|
|
1369
|
-
return;
|
|
1370
|
-
}
|
|
1371
|
-
if (((_b = response.result) === null || _b === void 0 ? void 0 : _b.ok) === false) {
|
|
1372
|
-
res.status(400).json(response.result);
|
|
1373
|
-
return;
|
|
1374
|
-
}
|
|
1375
|
-
res.json(response.result);
|
|
1376
|
-
}));
|
|
1377
|
-
api.get('/api/windows/:sessionId/logs', (req, res) => __awaiter(this, void 0, void 0, function* () {
|
|
1378
|
-
var _a;
|
|
1379
|
-
const sessionId = String((_a = req.params.sessionId) !== null && _a !== void 0 ? _a : '').trim();
|
|
1380
|
-
const response = yield callAgentRendererApi(sessionId, 'getLogs');
|
|
1381
|
-
if (!response.ok) {
|
|
1382
|
-
res.status(response.status).json({ ok: false, error: response.error });
|
|
1383
|
-
return;
|
|
1384
|
-
}
|
|
1385
|
-
res.json({ ok: true, logs: response.result });
|
|
1386
|
-
}));
|
|
1387
|
-
api.get('/api/spec', (_req, res) => {
|
|
1388
|
-
const address = agentApiServer === null || agentApiServer === void 0 ? void 0 : agentApiServer.address();
|
|
1389
|
-
const port = address && typeof address !== 'string' ? address.port : AGENT_API_PORT;
|
|
1390
|
-
res.json(getAgentApiSpec(port));
|
|
423
|
+
(0, routes_1.registerAgentApiRoutes)(api, {
|
|
424
|
+
host: AGENT_API_HOST,
|
|
425
|
+
port: AGENT_API_PORT,
|
|
426
|
+
version: electron_1.app.getVersion(),
|
|
427
|
+
getAgentWorkSessions,
|
|
428
|
+
isAgentApiReadyInSession,
|
|
429
|
+
agentControlledSessionCount: () => agentControlledSessions.size,
|
|
430
|
+
createWorkspaceId,
|
|
431
|
+
createWindow,
|
|
432
|
+
broadcastWorkspaceWindowsUpdated,
|
|
433
|
+
waitForAgentSessionReady,
|
|
434
|
+
getAgentApiBaseUrl,
|
|
435
|
+
callFirstReadyAgentRendererApi,
|
|
436
|
+
callAgentRendererApi,
|
|
437
|
+
getWindowByWorkspaceId,
|
|
438
|
+
setSessionAgentControlMode,
|
|
439
|
+
closeSession: closeSessionByWorkspaceId,
|
|
1391
440
|
});
|
|
1392
441
|
agentApiServer = api.listen(AGENT_API_PORT, AGENT_API_HOST, () => {
|
|
1393
442
|
console.log(`[Agent API] Listening on http://${AGENT_API_HOST}:${AGENT_API_PORT}`);
|
|
@@ -1420,7 +469,27 @@ function cleanupWindowFolder(webContentsId) {
|
|
|
1420
469
|
if (!folderId)
|
|
1421
470
|
return;
|
|
1422
471
|
windowFolders.delete(webContentsId);
|
|
1423
|
-
|
|
472
|
+
const owners = folderOwners.get(folderId);
|
|
473
|
+
if (!owners)
|
|
474
|
+
return;
|
|
475
|
+
owners.delete(webContentsId);
|
|
476
|
+
if (owners.size === 0) {
|
|
477
|
+
folderOwners.delete(folderId);
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
function pruneStaleFolderOwners(folderId) {
|
|
481
|
+
const owners = folderOwners.get(folderId);
|
|
482
|
+
if (!owners)
|
|
483
|
+
return;
|
|
484
|
+
for (const ownerId of [...owners]) {
|
|
485
|
+
const ownerWin = electron_1.BrowserWindow.getAllWindows().find(w => w.webContents.id === ownerId);
|
|
486
|
+
const ownerAlive = Boolean(ownerWin) && !ownerWin.isDestroyed() && !ownerWin.webContents.isDestroyed();
|
|
487
|
+
if (!ownerAlive) {
|
|
488
|
+
owners.delete(ownerId);
|
|
489
|
+
windowFolders.delete(ownerId);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
if (owners.size === 0) {
|
|
1424
493
|
folderOwners.delete(folderId);
|
|
1425
494
|
}
|
|
1426
495
|
}
|
|
@@ -1457,6 +526,8 @@ function telegramSendMessageViaBot(botToken, chatId, text) {
|
|
|
1457
526
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1458
527
|
const apiUrl = `https://api.telegram.org/bot${encodeURIComponent(botToken)}/sendMessage`;
|
|
1459
528
|
const safeText = text.length > 4096 ? `${text.slice(0, 4090)}…` : text;
|
|
529
|
+
const controller = new AbortController();
|
|
530
|
+
const timeoutId = setTimeout(() => controller.abort(), 30000);
|
|
1460
531
|
try {
|
|
1461
532
|
const res = yield fetch(apiUrl, {
|
|
1462
533
|
method: 'POST',
|
|
@@ -1465,6 +536,7 @@ function telegramSendMessageViaBot(botToken, chatId, text) {
|
|
|
1465
536
|
chat_id: chatId,
|
|
1466
537
|
text: safeText,
|
|
1467
538
|
}),
|
|
539
|
+
signal: controller.signal,
|
|
1468
540
|
});
|
|
1469
541
|
const data = (yield res.json());
|
|
1470
542
|
if (!data.ok) {
|
|
@@ -1481,6 +553,9 @@ function telegramSendMessageViaBot(botToken, chatId, text) {
|
|
|
1481
553
|
error: e instanceof Error ? e.message : String(e),
|
|
1482
554
|
};
|
|
1483
555
|
}
|
|
556
|
+
finally {
|
|
557
|
+
clearTimeout(timeoutId);
|
|
558
|
+
}
|
|
1484
559
|
});
|
|
1485
560
|
}
|
|
1486
561
|
function abortPendingGoogleAuth(reason) {
|
|
@@ -1562,7 +637,11 @@ function buildAppMenu() {
|
|
|
1562
637
|
});
|
|
1563
638
|
electron_1.Menu.setApplicationMenu(electron_1.Menu.buildFromTemplate(template));
|
|
1564
639
|
}
|
|
1565
|
-
function createWindow(workspaceId = createWorkspaceId()) {
|
|
640
|
+
function createWindow(workspaceId = createWorkspaceId(), options) {
|
|
641
|
+
closedWorkspaceIds.delete(workspaceId);
|
|
642
|
+
if (options === null || options === void 0 ? void 0 : options.agentControlled) {
|
|
643
|
+
agentControlledSessions.add(workspaceId);
|
|
644
|
+
}
|
|
1566
645
|
const win = new electron_1.BrowserWindow({
|
|
1567
646
|
width: 1000,
|
|
1568
647
|
height: 800,
|
|
@@ -1584,6 +663,10 @@ function createWindow(workspaceId = createWorkspaceId()) {
|
|
|
1584
663
|
win.on('closed', () => {
|
|
1585
664
|
var _a;
|
|
1586
665
|
closingWindowIds.delete(win.webContents.id);
|
|
666
|
+
const closedWorkspaceId = windowWorkspaces.get(win.webContents.id);
|
|
667
|
+
if (closedWorkspaceId) {
|
|
668
|
+
agentControlledSessions.delete(closedWorkspaceId);
|
|
669
|
+
}
|
|
1587
670
|
windows.delete(win);
|
|
1588
671
|
cleanupWindowFolder(win.webContents.id);
|
|
1589
672
|
windowWorkspaces.delete(win.webContents.id);
|
|
@@ -1652,7 +735,10 @@ function createWindow(workspaceId = createWorkspaceId()) {
|
|
|
1652
735
|
});
|
|
1653
736
|
// We need to point to the webpack output html
|
|
1654
737
|
win.loadFile(path.join(__dirname, 'index.html'), {
|
|
1655
|
-
query: {
|
|
738
|
+
query: {
|
|
739
|
+
workspaceId,
|
|
740
|
+
agentControlled: (options === null || options === void 0 ? void 0 : options.agentControlled) ? '1' : '0',
|
|
741
|
+
},
|
|
1656
742
|
});
|
|
1657
743
|
// DevTools disabled by default as requested
|
|
1658
744
|
// win.webContents.openDevTools();
|
|
@@ -1693,8 +779,12 @@ if (gotSingleInstanceLock)
|
|
|
1693
779
|
};
|
|
1694
780
|
const writeConfig = (config) => {
|
|
1695
781
|
try {
|
|
1696
|
-
|
|
1697
|
-
|
|
782
|
+
const sanitizedConfig = Object.assign({}, config);
|
|
783
|
+
if (Array.isArray(sanitizedConfig.workspaceSnapshots) && closedWorkspaceIds.size > 0) {
|
|
784
|
+
sanitizedConfig.workspaceSnapshots = sanitizedConfig.workspaceSnapshots.filter((item) => { var _a; return !closedWorkspaceIds.has(String((_a = item === null || item === void 0 ? void 0 : item.id) !== null && _a !== void 0 ? _a : '')); });
|
|
785
|
+
}
|
|
786
|
+
fs.writeFileSync(configPath, JSON.stringify(sanitizedConfig));
|
|
787
|
+
broadcastConfigUpdated(sanitizedConfig);
|
|
1698
788
|
return true;
|
|
1699
789
|
}
|
|
1700
790
|
catch (_a) {
|
|
@@ -1725,6 +815,8 @@ if (gotSingleInstanceLock)
|
|
|
1725
815
|
const id = String((_a = snapshot === null || snapshot === void 0 ? void 0 : snapshot.id) !== null && _a !== void 0 ? _a : '').trim();
|
|
1726
816
|
if (!id)
|
|
1727
817
|
return { ok: false };
|
|
818
|
+
if (closedWorkspaceIds.has(id))
|
|
819
|
+
return { ok: false, error: 'Workspace is closed.' };
|
|
1728
820
|
const config = readConfig();
|
|
1729
821
|
const current = Array.isArray(config.workspaceSnapshots) ? config.workspaceSnapshots : [];
|
|
1730
822
|
const nextSnapshot = Object.assign(Object.assign({}, snapshot), { id, updatedAt: typeof snapshot.updatedAt === 'string' ? snapshot.updatedAt : new Date().toISOString() });
|
|
@@ -1742,17 +834,14 @@ if (gotSingleInstanceLock)
|
|
|
1742
834
|
cleanupWindowFolder(webContentsId);
|
|
1743
835
|
return { ok: true };
|
|
1744
836
|
}
|
|
1745
|
-
|
|
1746
|
-
if (currentOwner && currentOwner !== webContentsId) {
|
|
1747
|
-
focusWindowByWebContentsId(currentOwner);
|
|
1748
|
-
return {
|
|
1749
|
-
ok: false,
|
|
1750
|
-
reason: 'occupied',
|
|
1751
|
-
folderId,
|
|
1752
|
-
};
|
|
1753
|
-
}
|
|
837
|
+
pruneStaleFolderOwners(folderId);
|
|
1754
838
|
cleanupWindowFolder(webContentsId);
|
|
1755
|
-
folderOwners.
|
|
839
|
+
let owners = folderOwners.get(folderId);
|
|
840
|
+
if (!owners) {
|
|
841
|
+
owners = new Set();
|
|
842
|
+
folderOwners.set(folderId, owners);
|
|
843
|
+
}
|
|
844
|
+
owners.add(webContentsId);
|
|
1756
845
|
windowFolders.set(webContentsId, folderId);
|
|
1757
846
|
broadcastWorkspaceWindowsUpdated();
|
|
1758
847
|
return { ok: true };
|
|
@@ -1771,21 +860,22 @@ if (gotSingleInstanceLock)
|
|
|
1771
860
|
return { ok: true };
|
|
1772
861
|
}));
|
|
1773
862
|
electron_1.ipcMain.handle('set-workspace-window-meta', (event, payload) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1774
|
-
var _a, _b, _c;
|
|
863
|
+
var _a, _b, _c, _d;
|
|
1775
864
|
const webContentsId = event.sender.id;
|
|
1776
865
|
const title = String((_a = payload === null || payload === void 0 ? void 0 : payload.title) !== null && _a !== void 0 ? _a : '').trim();
|
|
1777
866
|
if (title) {
|
|
1778
867
|
windowTitles.set(webContentsId, title);
|
|
1779
868
|
const win = electron_1.BrowserWindow.fromWebContents(event.sender);
|
|
1780
|
-
|
|
1781
|
-
|
|
869
|
+
const workspaceId = (_b = windowWorkspaces.get(webContentsId)) !== null && _b !== void 0 ? _b : '';
|
|
870
|
+
if (win && !win.isDestroyed() && workspaceId) {
|
|
871
|
+
syncWindowTitleForAgentControl(win, workspaceId);
|
|
1782
872
|
}
|
|
1783
873
|
}
|
|
1784
|
-
const folderId = String((
|
|
874
|
+
const folderId = String((_c = payload === null || payload === void 0 ? void 0 : payload.folderId) !== null && _c !== void 0 ? _c : '').trim();
|
|
1785
875
|
if (folderId) {
|
|
1786
876
|
windowFolders.set(webContentsId, folderId);
|
|
1787
877
|
}
|
|
1788
|
-
const folderPath = String((
|
|
878
|
+
const folderPath = String((_d = payload === null || payload === void 0 ? void 0 : payload.folderPath) !== null && _d !== void 0 ? _d : '').trim();
|
|
1789
879
|
if (folderPath) {
|
|
1790
880
|
windowFolderPaths.set(webContentsId, folderPath);
|
|
1791
881
|
}
|
|
@@ -1807,19 +897,31 @@ if (gotSingleInstanceLock)
|
|
|
1807
897
|
if (!win || win.isDestroyed())
|
|
1808
898
|
return { ok: false };
|
|
1809
899
|
const workspaceId = windowWorkspaces.get(id);
|
|
900
|
+
if (!workspaceId) {
|
|
901
|
+
closingWindowIds.add(id);
|
|
902
|
+
broadcastWorkspaceWindowsUpdated();
|
|
903
|
+
win.destroy();
|
|
904
|
+
setImmediate(() => broadcastWorkspaceWindowsUpdated());
|
|
905
|
+
return { ok: true };
|
|
906
|
+
}
|
|
907
|
+
return closeSessionByWorkspaceId(workspaceId);
|
|
908
|
+
}));
|
|
909
|
+
electron_1.ipcMain.handle('close-all-workspace-windows', () => __awaiter(void 0, void 0, void 0, function* () { return closeAllWorkspaceSessions(); }));
|
|
910
|
+
electron_1.ipcMain.handle('close-other-workspace-windows', (event) => __awaiter(void 0, void 0, void 0, function* () { return closeOtherWorkspaceSessions(event.sender.id); }));
|
|
911
|
+
electron_1.ipcMain.handle('get-agent-control-mode', (event) => __awaiter(void 0, void 0, void 0, function* () {
|
|
912
|
+
var _a;
|
|
913
|
+
const workspaceId = (_a = windowWorkspaces.get(event.sender.id)) !== null && _a !== void 0 ? _a : '';
|
|
914
|
+
return {
|
|
915
|
+
mode: workspaceId && agentControlledSessions.has(workspaceId) ? 'agent' : 'manual',
|
|
916
|
+
};
|
|
917
|
+
}));
|
|
918
|
+
electron_1.ipcMain.handle('release-agent-control', (event) => __awaiter(void 0, void 0, void 0, function* () {
|
|
919
|
+
var _a;
|
|
920
|
+
const workspaceId = (_a = windowWorkspaces.get(event.sender.id)) !== null && _a !== void 0 ? _a : '';
|
|
1810
921
|
if (workspaceId) {
|
|
1811
|
-
|
|
1812
|
-
const current = Array.isArray(config.workspaceSnapshots) ? config.workspaceSnapshots : [];
|
|
1813
|
-
const next = current.filter((item) => (item === null || item === void 0 ? void 0 : item.id) !== workspaceId);
|
|
1814
|
-
if (next.length !== current.length) {
|
|
1815
|
-
writeConfig(Object.assign(Object.assign({}, config), { workspaceSnapshots: next }));
|
|
1816
|
-
}
|
|
922
|
+
setSessionAgentControlMode(workspaceId, 'manual');
|
|
1817
923
|
}
|
|
1818
|
-
|
|
1819
|
-
broadcastWorkspaceWindowsUpdated();
|
|
1820
|
-
win.destroy();
|
|
1821
|
-
setImmediate(() => broadcastWorkspaceWindowsUpdated());
|
|
1822
|
-
return { ok: true };
|
|
924
|
+
return { ok: true, mode: 'manual' };
|
|
1823
925
|
}));
|
|
1824
926
|
electron_1.ipcMain.handle('telegram-send-test', (_event, payload) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1825
927
|
var _a, _b;
|