loadtoagent 1.1.0 → 1.3.1
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/README.ko.md +7 -0
- package/README.md +7 -0
- package/README.zh-CN.md +7 -0
- package/docs/PROVIDER-CONTRACTS.md +23 -1
- package/docs/UI-AUDIT-100.md +118 -0
- package/docs/UI-AUDIT-101-200.md +120 -0
- package/docs/UI-AUDIT-201-300.md +120 -0
- package/main.js +135 -38
- package/package.json +13 -4
- package/preload.js +6 -0
- package/renderer/app-agent-actions.js +125 -53
- package/renderer/app-bootstrap.js +54 -19
- package/renderer/app-dashboard.js +190 -55
- package/renderer/app-drawer-content.js +48 -44
- package/renderer/app-drawer-data.js +26 -13
- package/renderer/app-drawer.js +72 -54
- package/renderer/app-events-dialogs.js +146 -37
- package/renderer/app-events-filters.js +172 -13
- package/renderer/app-events-navigation.js +77 -30
- package/renderer/app-events-sessions.js +156 -9
- package/renderer/app-events.js +2 -1
- package/renderer/app-graph-model.js +20 -38
- package/renderer/app-graph-orchestration.js +15 -13
- package/renderer/app-graph-view.js +228 -113
- package/renderer/app-management.js +211 -0
- package/renderer/app-provider-visibility.js +124 -0
- package/renderer/app-quality.js +568 -0
- package/renderer/app-run-modal.js +103 -33
- package/renderer/app-runtime-overview.js +312 -0
- package/renderer/app-session-render.js +94 -37
- package/renderer/app-tmux-render.js +52 -44
- package/renderer/app.js +153 -75
- package/renderer/i18n-messages.js +833 -16
- package/renderer/i18n.js +104 -0
- package/renderer/index.html +145 -59
- package/renderer/shared.js +17 -0
- package/renderer/styles-agent-map.css +6 -0
- package/renderer/styles-cards.css +26 -0
- package/renderer/styles-components.css +86 -9
- package/renderer/styles-management.css +355 -0
- package/renderer/styles-overlays.css +8 -2
- package/renderer/styles-product.css +24 -16
- package/renderer/styles-quality.css +312 -0
- package/renderer/styles-readability.css +862 -0
- package/renderer/styles-responsive-product.css +84 -12
- package/renderer/styles-responsive-runtime.css +22 -14
- package/renderer/styles-responsive-shell.css +44 -48
- package/renderer/styles-responsive-workflows.css +37 -3
- package/renderer/styles-run-composer.css +5 -0
- package/renderer/styles-runtime-overview.css +285 -0
- package/renderer/styles-settings.css +160 -0
- package/renderer/styles-terminal.css +339 -2
- package/renderer/styles-workflow-map.css +362 -15
- package/renderer/styles-workflows.css +69 -2
- package/renderer/styles.css +27 -12
- package/renderer/terminal-agent.js +17 -13
- package/renderer/terminal-events.js +233 -40
- package/renderer/terminal-workbench.js +165 -77
- package/renderer/terminal.js +341 -34
- package/src/agentMonitor/claudeParser.js +96 -7
- package/src/agentMonitor/codexParser.js +59 -4
- package/src/agentMonitor/executionActivity.js +282 -0
- package/src/agentMonitor/genericParser.js +56 -13
- package/src/agentMonitor/hierarchy.js +17 -0
- package/src/agentMonitor/responseIntent.js +51 -0
- package/src/agentMonitor.js +21 -3
- package/src/agentRunner.js +66 -1
- package/src/attentionNotifier.js +14 -10
- package/src/automationMonitor.js +258 -0
- package/src/contracts.js +10 -1
- package/src/ipc/registerAgentIpc.js +9 -3
- package/src/ipc/registerAppIpc.js +4 -1
- package/src/ipc/registerTerminalIpc.js +12 -6
- package/src/macUpdateHelper.js +210 -0
- package/src/monitorWorker.js +65 -6
- package/src/platformPath.js +80 -0
- package/src/processMonitor.js +80 -22
- package/src/providerVisibilityStore.js +45 -0
- package/src/sessionIntelligence.js +247 -0
- package/src/terminalHost.js +381 -0
- package/src/terminalHostDaemon.js +60 -0
- package/src/terminalManager.js +158 -3
- package/src/updateInstaller.js +175 -0
package/main.js
CHANGED
|
@@ -10,10 +10,11 @@ const { execFileSync } = require('child_process');
|
|
|
10
10
|
const { AgentRunner, probeProviders } = require('./src/agentRunner');
|
|
11
11
|
const { providerList, blankUsage } = require('./src/providerRegistry');
|
|
12
12
|
const { TerminalManager } = require('./src/terminalManager');
|
|
13
|
+
const { TerminalHostClient, launchTerminalHost } = require('./src/terminalHost');
|
|
13
14
|
const { TmuxController } = require('./src/tmuxController');
|
|
14
15
|
const { normalizeWslList } = require('./src/tmuxMonitor');
|
|
15
|
-
const { BridgeServer } = require('./src/bridgeServer');
|
|
16
16
|
const { UpdateManager } = require('./src/updateManager');
|
|
17
|
+
const { launchDownloadedUpdate } = require('./src/updateInstaller');
|
|
17
18
|
const { readWorkspaces, removeWorkspace, writeWorkspaces } = require('./src/workspaceStore');
|
|
18
19
|
const { registerAppIpc } = require('./src/ipc/registerAppIpc');
|
|
19
20
|
const { registerAgentIpc } = require('./src/ipc/registerAgentIpc');
|
|
@@ -22,6 +23,8 @@ const { registerTmuxIpc } = require('./src/ipc/registerTmuxIpc');
|
|
|
22
23
|
const { registerWorkspaceIpc } = require('./src/ipc/registerWorkspaceIpc');
|
|
23
24
|
const { reportRecoverableError } = require('./src/diagnostics');
|
|
24
25
|
const { AttentionNotifier } = require('./src/attentionNotifier');
|
|
26
|
+
const { ProviderVisibilityStore } = require('./src/providerVisibilityStore');
|
|
27
|
+
const { macPathEntries } = require('./src/platformPath');
|
|
25
28
|
|
|
26
29
|
const PRODUCT_NAME = 'LoadToAgent';
|
|
27
30
|
app.setName(PRODUCT_NAME);
|
|
@@ -33,43 +36,45 @@ let mainWindow = null;
|
|
|
33
36
|
let monitorWorker = null;
|
|
34
37
|
let runner = null;
|
|
35
38
|
let terminalManager = null;
|
|
36
|
-
let bridgeServer = null;
|
|
37
39
|
let bridgeLauncher = null;
|
|
38
40
|
let backgroundTray = null;
|
|
39
41
|
let updateManager = null;
|
|
40
42
|
let attentionNotifier = null;
|
|
41
43
|
let isQuitting = false;
|
|
42
44
|
let appLocale = 'ko';
|
|
45
|
+
let providerVisibilityStore = null;
|
|
46
|
+
let pendingAttentionSessionId = '';
|
|
47
|
+
let rendererBootstrapped = false;
|
|
43
48
|
const tmuxController = new TmuxController({ platform: process.platform });
|
|
44
49
|
let availability = {};
|
|
45
50
|
let detailRequestId = 0;
|
|
46
51
|
const pendingDetails = new Map();
|
|
47
52
|
const MAIN_COPY = {
|
|
48
53
|
ko: {
|
|
49
|
-
trayTooltip: 'LoadToAgent · 백그라운드
|
|
54
|
+
trayTooltip: 'LoadToAgent · 백그라운드 터미널 {count}개',
|
|
50
55
|
trayOpen: 'LoadToAgent 열기',
|
|
51
|
-
traySessions: '백그라운드
|
|
52
|
-
trayQuit: '프로그램 끝내기 ·
|
|
56
|
+
traySessions: '백그라운드 터미널 {count}개 유지 중',
|
|
57
|
+
trayQuit: '프로그램 끝내기 · 터미널 세션 유지',
|
|
53
58
|
addWorkspaces: 'AI 작업 폴더 선택',
|
|
54
59
|
pickWorkspace: '작업 폴더 선택',
|
|
55
60
|
attentionTitle: '내 확인이 필요합니다',
|
|
56
61
|
attentionBody: '{provider} · {title}',
|
|
57
62
|
},
|
|
58
63
|
en: {
|
|
59
|
-
trayTooltip: 'LoadToAgent · {count} background
|
|
64
|
+
trayTooltip: 'LoadToAgent · {count} background terminals',
|
|
60
65
|
trayOpen: 'Open LoadToAgent',
|
|
61
|
-
traySessions: '{count} background
|
|
62
|
-
trayQuit: 'Quit app ·
|
|
66
|
+
traySessions: '{count} background terminals active',
|
|
67
|
+
trayQuit: 'Quit app · Keep terminal sessions',
|
|
63
68
|
addWorkspaces: 'Choose AI workspaces',
|
|
64
69
|
pickWorkspace: 'Choose workspace',
|
|
65
70
|
attentionTitle: 'Your review is needed',
|
|
66
71
|
attentionBody: '{provider} · {title}',
|
|
67
72
|
},
|
|
68
73
|
'zh-CN': {
|
|
69
|
-
trayTooltip: 'LoadToAgent · {count}
|
|
74
|
+
trayTooltip: 'LoadToAgent · {count} 个后台终端',
|
|
70
75
|
trayOpen: '打开 LoadToAgent',
|
|
71
|
-
traySessions: '正在保持 {count}
|
|
72
|
-
trayQuit: '退出应用 ·
|
|
76
|
+
traySessions: '正在保持 {count} 个后台终端',
|
|
77
|
+
trayQuit: '退出应用 · 保留终端会话',
|
|
73
78
|
addWorkspaces: '选择 AI 工作文件夹',
|
|
74
79
|
pickWorkspace: '选择工作文件夹',
|
|
75
80
|
attentionTitle: '需要你的确认',
|
|
@@ -79,6 +84,7 @@ const MAIN_COPY = {
|
|
|
79
84
|
let lastSnapshot = {
|
|
80
85
|
generatedAt: new Date().toISOString(),
|
|
81
86
|
sessions: [],
|
|
87
|
+
automations: [],
|
|
82
88
|
tmux: { generatedAt: new Date().toISOString(), available: false, status: '확인 중', distros: [], summary: { distros: 0, sessions: 0, windows: 0, panes: 0, aiPanes: 0, linked: 0 } },
|
|
83
89
|
summary: {
|
|
84
90
|
providers: providerList().map(provider => ({ ...provider, installed: false, sessions: 0, active: 0, waiting: 0, subagents: 0, usage: blankUsage() })),
|
|
@@ -133,6 +139,30 @@ function listWorkspaces() {
|
|
|
133
139
|
return readWorkspaces(userFile('workspaces.json'));
|
|
134
140
|
}
|
|
135
141
|
|
|
142
|
+
function isProviderVisible(providerId) {
|
|
143
|
+
return providerVisibilityStore ? providerVisibilityStore.isVisible(providerId) : true;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function loadProviderVisibility() {
|
|
147
|
+
providerVisibilityStore = new ProviderVisibilityStore(
|
|
148
|
+
userFile('provider-visibility.json'),
|
|
149
|
+
providerList().map(provider => provider.id),
|
|
150
|
+
error => reportRecoverableError('provider-visibility-load', error),
|
|
151
|
+
);
|
|
152
|
+
return providerVisibilityStore.load();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function saveProviderVisibility(value = {}) {
|
|
156
|
+
if (!providerVisibilityStore) loadProviderVisibility();
|
|
157
|
+
const saved = providerVisibilityStore.save(value);
|
|
158
|
+
updateBackgroundTrayMenu();
|
|
159
|
+
return saved;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function visibleSnapshotSessions(snapshot = lastSnapshot) {
|
|
163
|
+
return { ...snapshot, sessions: (snapshot.sessions || []).filter(session => isProviderVisible(session.provider)) };
|
|
164
|
+
}
|
|
165
|
+
|
|
136
166
|
function saveWorkspaces(items) {
|
|
137
167
|
return writeWorkspaces(userFile('workspaces.json'), items);
|
|
138
168
|
}
|
|
@@ -154,18 +184,11 @@ function listWslDistros() {
|
|
|
154
184
|
|
|
155
185
|
function hydratePlatformPath() {
|
|
156
186
|
if (process.platform !== 'darwin') return;
|
|
157
|
-
|
|
158
|
-
try {
|
|
159
|
-
const shellPath = process.env.SHELL || '/bin/zsh';
|
|
160
|
-
const loginPath = execFileSync(shellPath, ['-lic', 'printf %s "$PATH"'], { encoding: 'utf8', timeout: 5_000 }).trim();
|
|
161
|
-
additions.unshift(...loginPath.split(path.delimiter));
|
|
162
|
-
} catch (error) {
|
|
163
|
-
reportRecoverableError('platform-path', error);
|
|
164
|
-
}
|
|
165
|
-
process.env.PATH = [...new Set([...additions, ...String(process.env.PATH || '').split(path.delimiter)].filter(Boolean))].join(path.delimiter);
|
|
187
|
+
process.env.PATH = macPathEntries(os.homedir(), process.env.PATH).join(path.delimiter);
|
|
166
188
|
}
|
|
167
189
|
|
|
168
190
|
function createWindow() {
|
|
191
|
+
rendererBootstrapped = false;
|
|
169
192
|
mainWindow = new BrowserWindow({
|
|
170
193
|
width: 1600,
|
|
171
194
|
height: 980,
|
|
@@ -182,13 +205,14 @@ function createWindow() {
|
|
|
182
205
|
},
|
|
183
206
|
});
|
|
184
207
|
mainWindow.setMenuBarVisibility(false);
|
|
208
|
+
mainWindow.webContents.on('did-start-loading', () => { rendererBootstrapped = false; });
|
|
185
209
|
mainWindow.loadFile(path.join(__dirname, 'renderer', 'index.html'));
|
|
186
210
|
mainWindow.webContents.setWindowOpenHandler(() => ({ action: 'deny' }));
|
|
187
211
|
const allowedUrl = pathToFileURL(path.join(__dirname, 'renderer', 'index.html')).href;
|
|
188
212
|
mainWindow.webContents.on('will-navigate', (event, url) => { if (url !== allowedUrl) event.preventDefault(); });
|
|
189
213
|
mainWindow.once('ready-to-show', () => mainWindow && mainWindow.show());
|
|
190
214
|
mainWindow.on('close', event => {
|
|
191
|
-
if (isQuitting || !
|
|
215
|
+
if (isQuitting || !backgroundTerminalSessions().length) return;
|
|
192
216
|
event.preventDefault();
|
|
193
217
|
mainWindow.hide();
|
|
194
218
|
ensureBackgroundTray();
|
|
@@ -196,9 +220,13 @@ function createWindow() {
|
|
|
196
220
|
mainWindow.on('closed', () => { mainWindow = null; });
|
|
197
221
|
}
|
|
198
222
|
|
|
199
|
-
function
|
|
223
|
+
function backgroundTerminalSessions() {
|
|
200
224
|
if (!terminalManager) return [];
|
|
201
|
-
return terminalManager.list().filter(session => session.
|
|
225
|
+
return terminalManager.list().filter(session => session.status === 'running' || session.status === 'starting');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function visibleTerminalSessions(sessions) {
|
|
229
|
+
return (sessions || []).filter(session => session.type !== 'agent' || isProviderVisible(session.provider));
|
|
202
230
|
}
|
|
203
231
|
|
|
204
232
|
function showMainWindow() {
|
|
@@ -210,7 +238,7 @@ function showMainWindow() {
|
|
|
210
238
|
|
|
211
239
|
function updateBackgroundTrayMenu() {
|
|
212
240
|
if (!backgroundTray) return;
|
|
213
|
-
const count =
|
|
241
|
+
const count = backgroundTerminalSessions().length;
|
|
214
242
|
backgroundTray.setToolTip(mainText('trayTooltip', { count }));
|
|
215
243
|
backgroundTray.setContextMenu(Menu.buildFromTemplate([
|
|
216
244
|
{ label: mainText('trayOpen'), click: showMainWindow },
|
|
@@ -268,16 +296,33 @@ function sendSnapshot(snapshot) {
|
|
|
268
296
|
}
|
|
269
297
|
|
|
270
298
|
function openAttentionSession(session) {
|
|
299
|
+
if (!isProviderVisible(session && session.provider)) return;
|
|
300
|
+
pendingAttentionSessionId = String(session && session.id || '');
|
|
271
301
|
showMainWindow();
|
|
272
302
|
if (!mainWindow || mainWindow.isDestroyed()) return;
|
|
273
303
|
mainWindow.flashFrame(false);
|
|
304
|
+
if (!rendererBootstrapped || mainWindow.webContents.isLoadingMainFrame()) return;
|
|
274
305
|
try {
|
|
275
|
-
mainWindow.webContents.send('agents:attention-requested', { sessionId:
|
|
306
|
+
mainWindow.webContents.send('agents:attention-requested', { sessionId: pendingAttentionSessionId });
|
|
307
|
+
pendingAttentionSessionId = '';
|
|
276
308
|
} catch (error) {
|
|
277
309
|
reportRecoverableError('ipc-send:agents:attention-requested', error);
|
|
278
310
|
}
|
|
279
311
|
}
|
|
280
312
|
|
|
313
|
+
function markRendererReady() {
|
|
314
|
+
rendererBootstrapped = true;
|
|
315
|
+
if (!pendingAttentionSessionId || !mainWindow || mainWindow.isDestroyed()) return { ok: true };
|
|
316
|
+
const sessionId = pendingAttentionSessionId;
|
|
317
|
+
try {
|
|
318
|
+
mainWindow.webContents.send('agents:attention-requested', { sessionId });
|
|
319
|
+
pendingAttentionSessionId = '';
|
|
320
|
+
} catch (error) {
|
|
321
|
+
reportRecoverableError('ipc-send:agents:attention-requested', error);
|
|
322
|
+
}
|
|
323
|
+
return { ok: true };
|
|
324
|
+
}
|
|
325
|
+
|
|
281
326
|
function createAttentionNotifier() {
|
|
282
327
|
return new AttentionNotifier({
|
|
283
328
|
Notification,
|
|
@@ -310,10 +355,46 @@ function installationType() {
|
|
|
310
355
|
return fs.existsSync(path.join(__dirname, '.git')) ? 'source' : 'npm';
|
|
311
356
|
}
|
|
312
357
|
|
|
313
|
-
function
|
|
358
|
+
async function installDownloadedUpdate() {
|
|
359
|
+
if (!updateManager) throw new Error('업데이트 관리자가 준비되지 않았습니다.');
|
|
360
|
+
const downloaded = await updateManager.download();
|
|
361
|
+
const outcome = await launchDownloadedUpdate({
|
|
362
|
+
platform: process.platform,
|
|
363
|
+
installType: process.env.PORTABLE_EXECUTABLE_FILE ? 'portable' : installationType(),
|
|
364
|
+
installerPath: downloaded.downloadedPath,
|
|
365
|
+
downloadsDir: path.join(app.getPath('userData'), 'updates'),
|
|
366
|
+
appPath: process.execPath,
|
|
367
|
+
parentPid: process.pid,
|
|
368
|
+
shell,
|
|
369
|
+
});
|
|
370
|
+
if (outcome.mode === 'automatic') {
|
|
371
|
+
isQuitting = true;
|
|
372
|
+
setImmediate(() => app.quit());
|
|
373
|
+
}
|
|
374
|
+
return { ...updateManager.getState(), installMode: outcome.mode };
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async function setupRuntime() {
|
|
378
|
+
loadProviderVisibility();
|
|
314
379
|
const runsDir = userFile('agent-runs');
|
|
315
380
|
runner = new AgentRunner({ runsDir });
|
|
316
|
-
|
|
381
|
+
const terminalStoreFile = userFile('terminal-sessions.json');
|
|
382
|
+
const terminalHostFile = userFile('terminal-host.json');
|
|
383
|
+
terminalManager = demoCapture
|
|
384
|
+
? new TerminalManager({
|
|
385
|
+
storeFile: terminalStoreFile,
|
|
386
|
+
onPersistenceError: (operation, error) => reportRecoverableError(`terminal-sessions:${operation}`, error),
|
|
387
|
+
})
|
|
388
|
+
: new TerminalHostClient({
|
|
389
|
+
discoveryFile: terminalHostFile,
|
|
390
|
+
spawnHost: () => launchTerminalHost({
|
|
391
|
+
executable: process.execPath,
|
|
392
|
+
script: path.join(__dirname, 'src', 'terminalHostDaemon.js'),
|
|
393
|
+
storeFile: terminalStoreFile,
|
|
394
|
+
discoveryFile: terminalHostFile,
|
|
395
|
+
bridgeHome,
|
|
396
|
+
}),
|
|
397
|
+
});
|
|
317
398
|
updateManager = new UpdateManager({
|
|
318
399
|
currentVersion: app.getVersion(),
|
|
319
400
|
platform: process.platform,
|
|
@@ -338,12 +419,16 @@ function setupRuntime() {
|
|
|
338
419
|
}
|
|
339
420
|
terminalManager.on('data', payload => sendTerminal('terminals:data', payload));
|
|
340
421
|
terminalManager.on('state', payload => {
|
|
341
|
-
|
|
422
|
+
if (!payload.session || payload.session.type !== 'agent' || isProviderVisible(payload.session.provider)) {
|
|
423
|
+
sendTerminal('terminals:state', { ...payload, sessions: visibleTerminalSessions(payload.sessions) });
|
|
424
|
+
}
|
|
342
425
|
updateBackgroundTrayMenu();
|
|
343
426
|
if (monitorWorker) monitorWorker.postMessage({ type: 'bridge-presence', bridges: bridgePresence() });
|
|
344
427
|
});
|
|
345
|
-
|
|
346
|
-
|
|
428
|
+
terminalManager.on('disconnect', () => {
|
|
429
|
+
sendTerminal('terminals:error', { id: '', message: '터미널 호스트 연결이 끊어졌습니다. 프로그램을 다시 시작하면 실행 중인 세션에 다시 연결합니다.' });
|
|
430
|
+
});
|
|
431
|
+
await terminalManager.connect();
|
|
347
432
|
availability = probeProviders();
|
|
348
433
|
monitorWorker = new Worker(path.join(__dirname, 'src', 'monitorWorker.js'), {
|
|
349
434
|
workerData: { runsDir, home: os.homedir(), intervalMs: 1200, availability },
|
|
@@ -352,7 +437,7 @@ function setupRuntime() {
|
|
|
352
437
|
monitorWorker.on('message', message => {
|
|
353
438
|
if (message && message.type === 'snapshot') {
|
|
354
439
|
lastSnapshot = message.snapshot;
|
|
355
|
-
attentionNotifier.sync(lastSnapshot);
|
|
440
|
+
attentionNotifier.sync(visibleSnapshotSessions(lastSnapshot));
|
|
356
441
|
sendSnapshot(lastSnapshot);
|
|
357
442
|
}
|
|
358
443
|
if (message && message.type === 'detail-result') {
|
|
@@ -373,10 +458,11 @@ function bridgePresence() {
|
|
|
373
458
|
if (!terminalManager) return [];
|
|
374
459
|
const environment = process.platform === 'win32' ? 'windows' : (process.platform === 'darwin' ? 'macos' : 'linux');
|
|
375
460
|
return terminalManager.list()
|
|
376
|
-
.filter(session => session.type === 'agent' && session.status === 'running')
|
|
461
|
+
.filter(session => session.type === 'agent' && (session.status === 'running' || session.status === 'starting'))
|
|
377
462
|
.map(session => ({
|
|
378
463
|
id: session.bridgeId || session.id,
|
|
379
464
|
bridgeId: session.bridgeId || '',
|
|
465
|
+
linkedSessionId: session.bridgeId || '',
|
|
380
466
|
terminalId: session.id,
|
|
381
467
|
provider: session.provider,
|
|
382
468
|
pid: session.pid,
|
|
@@ -401,17 +487,20 @@ function bootstrapState() {
|
|
|
401
487
|
id: process.platform,
|
|
402
488
|
label: process.platform === 'darwin' ? 'macOS' : (process.platform === 'win32' ? 'Windows' : 'Linux'),
|
|
403
489
|
localShell: process.platform === 'win32' ? 'powershell' : 'shell',
|
|
404
|
-
localShellLabel: process.platform === 'darwin' ? '
|
|
490
|
+
localShellLabel: process.platform === 'darwin' ? 'macOS shell' : (process.platform === 'win32' ? 'Windows PowerShell' : 'Linux shell'),
|
|
405
491
|
nativeTmux: process.platform !== 'win32',
|
|
406
492
|
},
|
|
407
493
|
bridgeCli: bridgeLauncher,
|
|
408
494
|
update: updateManager ? updateManager.getState() : null,
|
|
495
|
+
providerVisibility: providerVisibilityStore ? providerVisibilityStore.snapshot() : { hidden: [] },
|
|
409
496
|
};
|
|
410
497
|
}
|
|
411
498
|
|
|
412
499
|
function requestAgentDetail(sessionId) {
|
|
413
500
|
return new Promise(resolve => {
|
|
414
501
|
if (!monitorWorker || String(sessionId || '').length > 500) return resolve(null);
|
|
502
|
+
const card = (lastSnapshot.sessions || []).find(session => session.id === String(sessionId || ''));
|
|
503
|
+
if (card && !isProviderVisible(card.provider)) return resolve(null);
|
|
415
504
|
const requestId = ++detailRequestId;
|
|
416
505
|
const timer = setTimeout(() => {
|
|
417
506
|
if (!pendingDetails.has(requestId)) return;
|
|
@@ -432,9 +521,10 @@ function registerIpcHandlers() {
|
|
|
432
521
|
registerAppIpc({
|
|
433
522
|
handleTrusted,
|
|
434
523
|
bootstrap: bootstrapState,
|
|
524
|
+
rendererReady: markRendererReady,
|
|
435
525
|
backgroundState: () => ({
|
|
436
526
|
visible: Boolean(mainWindow && !mainWindow.isDestroyed() && mainWindow.isVisible()),
|
|
437
|
-
backgroundSessions:
|
|
527
|
+
backgroundSessions: backgroundTerminalSessions().length,
|
|
438
528
|
trayReady: Boolean(backgroundTray),
|
|
439
529
|
}),
|
|
440
530
|
show: () => { showMainWindow(); return { ok: true }; },
|
|
@@ -443,13 +533,16 @@ function registerIpcHandlers() {
|
|
|
443
533
|
updateBackgroundTrayMenu();
|
|
444
534
|
return { locale: appLocale };
|
|
445
535
|
},
|
|
536
|
+
setProviderVisibility: saveProviderVisibility,
|
|
446
537
|
updateManager: () => updateManager,
|
|
538
|
+
installUpdate: installDownloadedUpdate,
|
|
447
539
|
});
|
|
448
540
|
registerAgentIpc({
|
|
449
541
|
handleTrusted,
|
|
450
542
|
snapshot: () => { refreshMonitor(); return lastSnapshot; },
|
|
451
543
|
requestDetail: requestAgentDetail,
|
|
452
544
|
runner: () => runner,
|
|
545
|
+
isProviderVisible,
|
|
453
546
|
probeProviders: () => {
|
|
454
547
|
availability = probeProviders();
|
|
455
548
|
if (monitorWorker) monitorWorker.postMessage({ type: 'availability', availability });
|
|
@@ -462,6 +555,7 @@ function registerIpcHandlers() {
|
|
|
462
555
|
requireTrustedSender,
|
|
463
556
|
trustedSender,
|
|
464
557
|
manager: () => terminalManager,
|
|
558
|
+
isProviderVisible,
|
|
465
559
|
listWslDistros,
|
|
466
560
|
sendError: payload => sendTerminal('terminals:error', payload),
|
|
467
561
|
});
|
|
@@ -514,11 +608,14 @@ function registerIpcHandlers() {
|
|
|
514
608
|
|
|
515
609
|
registerIpcHandlers();
|
|
516
610
|
|
|
517
|
-
app.whenReady().then(() => {
|
|
611
|
+
app.whenReady().then(async () => {
|
|
518
612
|
hydratePlatformPath();
|
|
519
|
-
setupRuntime();
|
|
613
|
+
await setupRuntime();
|
|
520
614
|
createWindow();
|
|
521
615
|
app.on('activate', showMainWindow);
|
|
616
|
+
}).catch(error => {
|
|
617
|
+
dialog.showErrorBox('LoadToAgent 시작 실패', error.stack || error.message || String(error));
|
|
618
|
+
app.quit();
|
|
522
619
|
});
|
|
523
620
|
|
|
524
621
|
app.on('window-all-closed', () => {
|
|
@@ -528,8 +625,8 @@ app.on('window-all-closed', () => {
|
|
|
528
625
|
app.on('before-quit', () => {
|
|
529
626
|
isQuitting = true;
|
|
530
627
|
if (attentionNotifier) attentionNotifier.dispose();
|
|
531
|
-
if (
|
|
532
|
-
if (terminalManager) terminalManager.dispose();
|
|
628
|
+
if (terminalManager instanceof TerminalHostClient) terminalManager.dispose({ shutdownIfIdle: true });
|
|
629
|
+
else if (terminalManager) terminalManager.dispose({ preserveSessions: true });
|
|
533
630
|
if (monitorWorker) {
|
|
534
631
|
monitorWorker.postMessage({ type: 'stop' });
|
|
535
632
|
monitorWorker.terminate();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "loadtoagent",
|
|
3
|
-
"version": "1.1
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "A local desktop dashboard for Claude, Codex, Gemini, and Grok sessions.",
|
|
5
5
|
"main": "main.js",
|
|
6
6
|
"author": "wincube AX",
|
|
@@ -44,14 +44,20 @@
|
|
|
44
44
|
"start": "electron .",
|
|
45
45
|
"test": "node scripts/regression-test.js",
|
|
46
46
|
"test:terminal": "electron scripts/terminal-integration-test.js",
|
|
47
|
+
"test:terminal-restart": "node scripts/terminal-restart-persistence-check.js",
|
|
47
48
|
"test:bridge": "electron scripts/bridge-integration-test.js",
|
|
48
49
|
"test:tmux": "node scripts/tmux-integration-test.js",
|
|
49
50
|
"test:visual": "electron scripts/visual-check.js",
|
|
50
51
|
"test:responsive": "electron scripts/responsive-check.js",
|
|
51
52
|
"test:interaction": "electron scripts/interaction-check.js",
|
|
53
|
+
"test:scroll": "electron scripts/scroll-retention-check.js",
|
|
54
|
+
"audit:ui": "electron scripts/browser-audit-host.js",
|
|
55
|
+
"test:runtime-overview": "electron scripts/runtime-overview-visual.js",
|
|
56
|
+
"test:readability": "electron scripts/readability-visual-check.js",
|
|
57
|
+
"test:update:mac": "node scripts/mac-update-integration-test.js",
|
|
52
58
|
"demo:readme": "electron scripts/readme-demo.js",
|
|
53
59
|
"dist": "electron-builder",
|
|
54
|
-
"dist:win": "electron-builder --win",
|
|
60
|
+
"dist:win": "electron-builder --win --publish never",
|
|
55
61
|
"dist:mac": "electron-builder --mac --publish never",
|
|
56
62
|
"pack": "electron-builder --dir",
|
|
57
63
|
"prepublishOnly": "npm test"
|
|
@@ -90,6 +96,7 @@
|
|
|
90
96
|
"bin/**/*",
|
|
91
97
|
"node_modules/node-pty/**/*"
|
|
92
98
|
],
|
|
99
|
+
"afterPack": "scripts/after-pack.js",
|
|
93
100
|
"win": {
|
|
94
101
|
"icon": "build/icon.ico",
|
|
95
102
|
"target": [
|
|
@@ -123,13 +130,15 @@
|
|
|
123
130
|
"artifactName": "LoadToAgent-${version}-${arch}.${ext}"
|
|
124
131
|
},
|
|
125
132
|
"portable": {
|
|
126
|
-
"artifactName": "LoadToAgent-${version}-portable.exe"
|
|
133
|
+
"artifactName": "LoadToAgent-${version}-portable.exe",
|
|
134
|
+
"unpackDirName": false
|
|
127
135
|
},
|
|
128
136
|
"nsis": {
|
|
129
137
|
"artifactName": "LoadToAgent-Setup-${version}.exe",
|
|
130
138
|
"oneClick": false,
|
|
131
139
|
"perMachine": false,
|
|
132
|
-
"allowToChangeInstallationDirectory": true
|
|
140
|
+
"allowToChangeInstallationDirectory": true,
|
|
141
|
+
"runAfterFinish": false
|
|
133
142
|
}
|
|
134
143
|
}
|
|
135
144
|
}
|
package/preload.js
CHANGED
|
@@ -4,17 +4,23 @@ const { contextBridge, ipcRenderer } = require('electron');
|
|
|
4
4
|
|
|
5
5
|
contextBridge.exposeInMainWorld('loadtoagent', {
|
|
6
6
|
bootstrap: () => ipcRenderer.invoke('app:bootstrap'),
|
|
7
|
+
rendererReady: () => ipcRenderer.invoke('app:renderer-ready'),
|
|
7
8
|
backgroundState: () => ipcRenderer.invoke('app:background-state'),
|
|
8
9
|
showApp: () => ipcRenderer.invoke('app:show'),
|
|
9
10
|
setLocale: locale => ipcRenderer.invoke('app:set-locale', locale),
|
|
11
|
+
setProviderVisibility: preference => ipcRenderer.invoke('app:set-provider-visibility', preference),
|
|
10
12
|
checkForUpdate: () => ipcRenderer.invoke('app:update-check'),
|
|
11
13
|
downloadUpdate: () => ipcRenderer.invoke('app:update-download'),
|
|
12
14
|
openDownloadedUpdate: () => ipcRenderer.invoke('app:update-open'),
|
|
15
|
+
installDownloadedUpdate: () => ipcRenderer.invoke('app:update-install'),
|
|
13
16
|
openUpdateRelease: () => ipcRenderer.invoke('app:update-open-release'),
|
|
14
17
|
snapshot: () => ipcRenderer.invoke('agents:snapshot'),
|
|
15
18
|
sessionDetail: sessionId => ipcRenderer.invoke('agents:detail', sessionId),
|
|
16
19
|
runAgent: options => ipcRenderer.invoke('agents:run', options),
|
|
17
20
|
stopAgent: runId => ipcRenderer.invoke('agents:stop', runId),
|
|
21
|
+
pauseAgent: runId => ipcRenderer.invoke('agents:pause', runId),
|
|
22
|
+
resumeAgentRun: runId => ipcRenderer.invoke('agents:resume-run', runId),
|
|
23
|
+
retryAgent: runId => ipcRenderer.invoke('agents:retry', runId),
|
|
18
24
|
activeRuns: () => ipcRenderer.invoke('agents:active-runs'),
|
|
19
25
|
probeProviders: () => ipcRenderer.invoke('providers:probe'),
|
|
20
26
|
listWorkspaces: () => ipcRenderer.invoke('workspaces:list'),
|