@agenticmail/enterprise 0.5.86 → 0.5.88
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/chunk-5SKX72EH.js +898 -0
- package/dist/chunk-6YHJFTJV.js +898 -0
- package/dist/chunk-F23OR2UN.js +15813 -0
- package/dist/chunk-FOEQH5TZ.js +2191 -0
- package/dist/chunk-NRF3YRF7.js +198 -0
- package/dist/chunk-PGD43XUU.js +15813 -0
- package/dist/chunk-WY7EUYDG.js +2191 -0
- package/dist/cli.js +1 -1
- package/dist/dashboard/pages/agent-detail.js +50 -15
- package/dist/environment-QR43BVUX.js +11 -0
- package/dist/index.js +4 -4
- package/dist/routes-6YFW4RXP.js +6910 -0
- package/dist/routes-IUGAENMP.js +6871 -0
- package/dist/runtime-APPERXEP.js +49 -0
- package/dist/runtime-RZNTYFYL.js +49 -0
- package/dist/server-56SKZN5M.js +12 -0
- package/dist/server-A235D4XK.js +12 -0
- package/dist/setup-FDTYP4T2.js +20 -0
- package/dist/setup-R2EI7Y2P.js +20 -0
- package/package.json +1 -1
- package/src/dashboard/pages/agent-detail.js +50 -15
- package/src/engine/agent-routes.ts +50 -0
- package/src/runtime/environment.ts +12 -2
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AgentRuntime,
|
|
3
|
+
EmailChannel,
|
|
4
|
+
FollowUpScheduler,
|
|
5
|
+
SessionManager,
|
|
6
|
+
SubAgentManager,
|
|
7
|
+
ToolRegistry,
|
|
8
|
+
callLLM,
|
|
9
|
+
createAgentRuntime,
|
|
10
|
+
createNoopHooks,
|
|
11
|
+
createRuntimeHooks,
|
|
12
|
+
estimateMessageTokens,
|
|
13
|
+
estimateTokens,
|
|
14
|
+
executeTool,
|
|
15
|
+
runAgentLoop,
|
|
16
|
+
toolsToDefinitions
|
|
17
|
+
} from "./chunk-F23OR2UN.js";
|
|
18
|
+
import "./chunk-NRF3YRF7.js";
|
|
19
|
+
import "./chunk-TYW5XTOW.js";
|
|
20
|
+
import "./chunk-AQH4DFYV.js";
|
|
21
|
+
import "./chunk-JLSQOQ5L.js";
|
|
22
|
+
import {
|
|
23
|
+
PROVIDER_REGISTRY,
|
|
24
|
+
listAllProviders,
|
|
25
|
+
resolveApiKeyForProvider,
|
|
26
|
+
resolveProvider
|
|
27
|
+
} from "./chunk-67KZYSLU.js";
|
|
28
|
+
import "./chunk-KFQGP6VL.js";
|
|
29
|
+
export {
|
|
30
|
+
AgentRuntime,
|
|
31
|
+
EmailChannel,
|
|
32
|
+
FollowUpScheduler,
|
|
33
|
+
PROVIDER_REGISTRY,
|
|
34
|
+
SessionManager,
|
|
35
|
+
SubAgentManager,
|
|
36
|
+
ToolRegistry,
|
|
37
|
+
callLLM,
|
|
38
|
+
createAgentRuntime,
|
|
39
|
+
createNoopHooks,
|
|
40
|
+
createRuntimeHooks,
|
|
41
|
+
estimateMessageTokens,
|
|
42
|
+
estimateTokens,
|
|
43
|
+
executeTool,
|
|
44
|
+
listAllProviders,
|
|
45
|
+
resolveApiKeyForProvider,
|
|
46
|
+
resolveProvider,
|
|
47
|
+
runAgentLoop,
|
|
48
|
+
toolsToDefinitions
|
|
49
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AgentRuntime,
|
|
3
|
+
EmailChannel,
|
|
4
|
+
FollowUpScheduler,
|
|
5
|
+
SessionManager,
|
|
6
|
+
SubAgentManager,
|
|
7
|
+
ToolRegistry,
|
|
8
|
+
callLLM,
|
|
9
|
+
createAgentRuntime,
|
|
10
|
+
createNoopHooks,
|
|
11
|
+
createRuntimeHooks,
|
|
12
|
+
estimateMessageTokens,
|
|
13
|
+
estimateTokens,
|
|
14
|
+
executeTool,
|
|
15
|
+
runAgentLoop,
|
|
16
|
+
toolsToDefinitions
|
|
17
|
+
} from "./chunk-PGD43XUU.js";
|
|
18
|
+
import "./chunk-NRF3YRF7.js";
|
|
19
|
+
import "./chunk-TYW5XTOW.js";
|
|
20
|
+
import "./chunk-AQH4DFYV.js";
|
|
21
|
+
import "./chunk-JLSQOQ5L.js";
|
|
22
|
+
import {
|
|
23
|
+
PROVIDER_REGISTRY,
|
|
24
|
+
listAllProviders,
|
|
25
|
+
resolveApiKeyForProvider,
|
|
26
|
+
resolveProvider
|
|
27
|
+
} from "./chunk-67KZYSLU.js";
|
|
28
|
+
import "./chunk-KFQGP6VL.js";
|
|
29
|
+
export {
|
|
30
|
+
AgentRuntime,
|
|
31
|
+
EmailChannel,
|
|
32
|
+
FollowUpScheduler,
|
|
33
|
+
PROVIDER_REGISTRY,
|
|
34
|
+
SessionManager,
|
|
35
|
+
SubAgentManager,
|
|
36
|
+
ToolRegistry,
|
|
37
|
+
callLLM,
|
|
38
|
+
createAgentRuntime,
|
|
39
|
+
createNoopHooks,
|
|
40
|
+
createRuntimeHooks,
|
|
41
|
+
estimateMessageTokens,
|
|
42
|
+
estimateTokens,
|
|
43
|
+
executeTool,
|
|
44
|
+
listAllProviders,
|
|
45
|
+
resolveApiKeyForProvider,
|
|
46
|
+
resolveProvider,
|
|
47
|
+
runAgentLoop,
|
|
48
|
+
toolsToDefinitions
|
|
49
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createServer
|
|
3
|
+
} from "./chunk-FOEQH5TZ.js";
|
|
4
|
+
import "./chunk-3SMTCIR4.js";
|
|
5
|
+
import "./chunk-JLSQOQ5L.js";
|
|
6
|
+
import "./chunk-RO537U6H.js";
|
|
7
|
+
import "./chunk-DRXMYYKN.js";
|
|
8
|
+
import "./chunk-67KZYSLU.js";
|
|
9
|
+
import "./chunk-KFQGP6VL.js";
|
|
10
|
+
export {
|
|
11
|
+
createServer
|
|
12
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createServer
|
|
3
|
+
} from "./chunk-WY7EUYDG.js";
|
|
4
|
+
import "./chunk-3SMTCIR4.js";
|
|
5
|
+
import "./chunk-JLSQOQ5L.js";
|
|
6
|
+
import "./chunk-RO537U6H.js";
|
|
7
|
+
import "./chunk-DRXMYYKN.js";
|
|
8
|
+
import "./chunk-67KZYSLU.js";
|
|
9
|
+
import "./chunk-KFQGP6VL.js";
|
|
10
|
+
export {
|
|
11
|
+
createServer
|
|
12
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
promptCompanyInfo,
|
|
3
|
+
promptDatabase,
|
|
4
|
+
promptDeployment,
|
|
5
|
+
promptDomain,
|
|
6
|
+
promptRegistration,
|
|
7
|
+
provision,
|
|
8
|
+
runSetupWizard
|
|
9
|
+
} from "./chunk-5SKX72EH.js";
|
|
10
|
+
import "./chunk-QDXUZP7Y.js";
|
|
11
|
+
import "./chunk-KFQGP6VL.js";
|
|
12
|
+
export {
|
|
13
|
+
promptCompanyInfo,
|
|
14
|
+
promptDatabase,
|
|
15
|
+
promptDeployment,
|
|
16
|
+
promptDomain,
|
|
17
|
+
promptRegistration,
|
|
18
|
+
provision,
|
|
19
|
+
runSetupWizard
|
|
20
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import {
|
|
2
|
+
promptCompanyInfo,
|
|
3
|
+
promptDatabase,
|
|
4
|
+
promptDeployment,
|
|
5
|
+
promptDomain,
|
|
6
|
+
promptRegistration,
|
|
7
|
+
provision,
|
|
8
|
+
runSetupWizard
|
|
9
|
+
} from "./chunk-6YHJFTJV.js";
|
|
10
|
+
import "./chunk-QDXUZP7Y.js";
|
|
11
|
+
import "./chunk-KFQGP6VL.js";
|
|
12
|
+
export {
|
|
13
|
+
promptCompanyInfo,
|
|
14
|
+
promptDatabase,
|
|
15
|
+
promptDeployment,
|
|
16
|
+
promptDomain,
|
|
17
|
+
promptRegistration,
|
|
18
|
+
provision,
|
|
19
|
+
runSetupWizard
|
|
20
|
+
};
|
package/package.json
CHANGED
|
@@ -4022,6 +4022,8 @@ function MeetingCapabilitiesSection(props) {
|
|
|
4022
4022
|
if (cfg.meetingsEnabled) checkMeetingBrowser();
|
|
4023
4023
|
}, [cfg.meetingsEnabled]);
|
|
4024
4024
|
|
|
4025
|
+
var _stopping = useState(false); var stopping = _stopping[0]; var setStopping = _stopping[1];
|
|
4026
|
+
|
|
4025
4027
|
function launchMeetingBrowser() {
|
|
4026
4028
|
setLaunching(true);
|
|
4027
4029
|
engineCall('/bridge/agents/' + agentId + '/browser-config/launch-meeting-browser', { method: 'POST' })
|
|
@@ -4033,38 +4035,61 @@ function MeetingCapabilitiesSection(props) {
|
|
|
4033
4035
|
.catch(function(e) { toast(e.message, 'error'); setLaunching(false); });
|
|
4034
4036
|
}
|
|
4035
4037
|
|
|
4038
|
+
function stopMeetingBrowser() {
|
|
4039
|
+
setStopping(true);
|
|
4040
|
+
engineCall('/bridge/agents/' + agentId + '/browser-config/stop-meeting-browser', { method: 'POST' })
|
|
4041
|
+
.then(function(d) {
|
|
4042
|
+
if (d.error) { toast(d.error, 'error'); }
|
|
4043
|
+
else { toast('Meeting browser stopped', 'success'); setBrowserStatus(null); }
|
|
4044
|
+
setStopping(false);
|
|
4045
|
+
})
|
|
4046
|
+
.catch(function(e) { toast(e.message, 'error'); setStopping(false); });
|
|
4047
|
+
}
|
|
4048
|
+
|
|
4036
4049
|
var meetingsOn = cfg.meetingsEnabled === true;
|
|
4037
4050
|
|
|
4038
4051
|
var isContainer = sysCaps && sysCaps.raw && (sysCaps.raw.deployment === 'container');
|
|
4039
4052
|
var canJoinMeetings = sysCaps && sysCaps.raw && sysCaps.raw.canJoinMeetings;
|
|
4053
|
+
var isObserverOnly = sysCaps && sysCaps.raw && sysCaps.raw.isContainerWithFakeMedia;
|
|
4054
|
+
var canJoinFullMedia = sysCaps && sysCaps.raw && sysCaps.raw.canJoinMeetingsFullMedia;
|
|
4040
4055
|
|
|
4041
4056
|
return h('div', { style: sectionStyle },
|
|
4042
4057
|
sectionTitle('\uD83C\uDFA5', 'Meetings & Video Calls'),
|
|
4043
4058
|
|
|
4044
|
-
// Deployment capability warning
|
|
4045
|
-
sysCaps && !canJoinMeetings && h('div', { style: {
|
|
4046
|
-
background: 'rgba(
|
|
4059
|
+
// Deployment capability warning — show for no-meeting OR observer-only
|
|
4060
|
+
sysCaps && (!canJoinMeetings || isObserverOnly) && h('div', { style: {
|
|
4061
|
+
background: isObserverOnly ? 'rgba(33,150,243,0.08)' : 'rgba(255,152,0,0.08)',
|
|
4062
|
+
border: '1px solid ' + (isObserverOnly ? 'rgba(33,150,243,0.3)' : 'rgba(255,152,0,0.3)'),
|
|
4047
4063
|
borderRadius: 8, padding: '12px 16px', marginBottom: 16,
|
|
4048
4064
|
} },
|
|
4049
4065
|
h('div', { style: { display: 'flex', alignItems: 'flex-start', gap: 10 } },
|
|
4050
|
-
h('span', { style: { fontSize: 18 } }, '\u26A0\uFE0F'),
|
|
4066
|
+
h('span', { style: { fontSize: 18 } }, isObserverOnly ? '\uD83D\uDC41\uFE0F' : '\u26A0\uFE0F'),
|
|
4051
4067
|
h('div', null,
|
|
4052
4068
|
h('div', { style: { fontWeight: 600, fontSize: 13, marginBottom: 4 } },
|
|
4053
|
-
|
|
4069
|
+
isObserverOnly
|
|
4070
|
+
? 'Observer Mode — Container Deployment'
|
|
4071
|
+
: 'Limited on this deployment' + (isContainer ? ' (container)' : '')
|
|
4054
4072
|
),
|
|
4055
4073
|
h('div', { style: { fontSize: 12, color: 'var(--text-muted)', lineHeight: 1.5 } },
|
|
4056
|
-
|
|
4074
|
+
isObserverOnly
|
|
4075
|
+
? 'This container has Chromium + virtual display, but uses fake media devices. The agent can join meetings as an observer — it can see the screen, read chat, and take notes, but cannot send or receive real audio/video.'
|
|
4076
|
+
: 'Video meeting joining requires a display server, audio subsystem, and browser — which are not available on container deployments (Fly.io, Railway, etc.).'
|
|
4057
4077
|
),
|
|
4058
|
-
h('div', { style: { fontSize: 12, marginTop: 8, lineHeight: 1.5 } },
|
|
4078
|
+
isObserverOnly && h('div', { style: { fontSize: 12, marginTop: 8, lineHeight: 1.5 } },
|
|
4079
|
+
h('strong', null, 'Works in observer mode: '),
|
|
4080
|
+
'Join meetings, read chat, see shared screens, take screenshots, capture meeting notes.'
|
|
4081
|
+
),
|
|
4082
|
+
isObserverOnly && h('div', { style: { fontSize: 12, marginTop: 4, lineHeight: 1.5 } },
|
|
4083
|
+
h('strong', null, 'Does NOT work: '),
|
|
4084
|
+
'Speaking, sending audio, showing video/camera, screen sharing.'
|
|
4085
|
+
),
|
|
4086
|
+
!isObserverOnly && h('div', { style: { fontSize: 12, marginTop: 8, lineHeight: 1.5 } },
|
|
4059
4087
|
h('strong', null, 'What works here: '), 'Calendar management, meeting prep, Drive organization, notes, email scanning for invites, RSVP.'
|
|
4060
4088
|
),
|
|
4061
|
-
h('div', { style: { fontSize: 12, marginTop:
|
|
4062
|
-
h('strong', null, 'For
|
|
4089
|
+
h('div', { style: { fontSize: 12, marginTop: 8, lineHeight: 1.5 } },
|
|
4090
|
+
h('strong', null, 'For full media (audio + video): '), 'Deploy on a VM (Hetzner, DigitalOcean, GCP) with our ',
|
|
4063
4091
|
h('code', { style: { fontSize: 11, background: 'var(--bg-secondary)', padding: '1px 4px', borderRadius: 3 } }, 'vm-setup.sh'),
|
|
4064
|
-
' script, or use a Remote Browser (CDP) provider
|
|
4065
|
-
),
|
|
4066
|
-
sysCaps.unavailable && sysCaps.unavailable.length > 0 && h('div', { style: { fontSize: 11, color: 'var(--text-muted)', marginTop: 8 } },
|
|
4067
|
-
'Missing: ' + sysCaps.unavailable.join(' \u2022 ')
|
|
4092
|
+
' script, or use a Remote Browser (CDP) provider.'
|
|
4068
4093
|
)
|
|
4069
4094
|
)
|
|
4070
4095
|
)
|
|
@@ -4111,8 +4136,18 @@ function MeetingCapabilitiesSection(props) {
|
|
|
4111
4136
|
),
|
|
4112
4137
|
browserStatus?.ok
|
|
4113
4138
|
? h('div', null,
|
|
4114
|
-
h('div', { style: {
|
|
4115
|
-
|
|
4139
|
+
h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 } },
|
|
4140
|
+
h('span', { style: { fontSize: 12, color: 'var(--text-muted)' } }, browserStatus.browserVersion || 'Chromium ready'),
|
|
4141
|
+
isObserverOnly && h('span', { className: 'badge', style: { fontSize: 10, padding: '1px 6px', background: 'rgba(33,150,243,0.15)', color: 'var(--accent)' } }, 'Observer Only'),
|
|
4142
|
+
browserStatus.port && h('span', { style: { fontSize: 11, color: 'var(--text-muted)' } }, 'Port ' + browserStatus.port)
|
|
4143
|
+
),
|
|
4144
|
+
h('button', {
|
|
4145
|
+
className: 'btn btn-sm',
|
|
4146
|
+
disabled: stopping,
|
|
4147
|
+
onClick: stopMeetingBrowser,
|
|
4148
|
+
style: { background: 'var(--danger)', color: '#fff', border: 'none', marginTop: 4 },
|
|
4149
|
+
}, stopping ? 'Stopping...' : '\u23F9\uFE0F Stop Meeting Browser'),
|
|
4150
|
+
isContainer && !canJoinMeetings && !isObserverOnly && h('div', { style: { fontSize: 11, color: 'var(--warning)', marginTop: 4 } },
|
|
4116
4151
|
'\u26A0 Browser is headless-only on this container. It cannot join video calls (no display/audio).'
|
|
4117
4152
|
)
|
|
4118
4153
|
)
|
|
@@ -1060,6 +1060,56 @@ export function createAgentRoutes(opts: {
|
|
|
1060
1060
|
}
|
|
1061
1061
|
});
|
|
1062
1062
|
|
|
1063
|
+
/**
|
|
1064
|
+
* POST /bridge/agents/:id/browser-config/stop-meeting-browser
|
|
1065
|
+
* Kills the meeting browser process for this agent.
|
|
1066
|
+
*/
|
|
1067
|
+
router.post('/bridge/agents/:id/browser-config/stop-meeting-browser', async (c) => {
|
|
1068
|
+
const agentId = c.req.param('id');
|
|
1069
|
+
const managed = lifecycle.getAgent(agentId);
|
|
1070
|
+
if (!managed) return c.json({ error: 'Agent not found' }, 404);
|
|
1071
|
+
|
|
1072
|
+
try {
|
|
1073
|
+
const port = (managed.config as any)?.meetingBrowserPort;
|
|
1074
|
+
if (!port) return c.json({ error: 'No meeting browser is tracked for this agent' }, 400);
|
|
1075
|
+
|
|
1076
|
+
// Try to close gracefully via CDP
|
|
1077
|
+
let closed = false;
|
|
1078
|
+
try {
|
|
1079
|
+
const resp = await fetch(`http://127.0.0.1:${port}/json/version`, { signal: AbortSignal.timeout(2000) });
|
|
1080
|
+
if (resp.ok) {
|
|
1081
|
+
// Get the websocket URL and send Browser.close
|
|
1082
|
+
const data = await resp.json() as any;
|
|
1083
|
+
if (data.webSocketDebuggerUrl) {
|
|
1084
|
+
try {
|
|
1085
|
+
const closeResp = await fetch(`http://127.0.0.1:${port}/json/close`, { method: 'PUT', signal: AbortSignal.timeout(3000) });
|
|
1086
|
+
closed = true;
|
|
1087
|
+
} catch { /* fallback to kill */ }
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
} catch { /* not running or not reachable */ }
|
|
1091
|
+
|
|
1092
|
+
// Fallback: kill by port
|
|
1093
|
+
if (!closed) {
|
|
1094
|
+
try {
|
|
1095
|
+
const { execSync } = await import('node:child_process');
|
|
1096
|
+
execSync(`lsof -ti:${port} | xargs kill -9 2>/dev/null || true`, { timeout: 5000 });
|
|
1097
|
+
closed = true;
|
|
1098
|
+
} catch { /* already dead */ }
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
// Clear config
|
|
1102
|
+
delete (managed.config as any).meetingBrowserPort;
|
|
1103
|
+
delete (managed.config as any).meetingBrowserCdpUrl;
|
|
1104
|
+
managed.updatedAt = new Date().toISOString();
|
|
1105
|
+
await lifecycle.saveAgent(agentId);
|
|
1106
|
+
|
|
1107
|
+
return c.json({ ok: true, stopped: true, port });
|
|
1108
|
+
} catch (e: any) {
|
|
1109
|
+
return c.json({ error: 'Failed to stop meeting browser: ' + e.message }, 500);
|
|
1110
|
+
}
|
|
1111
|
+
});
|
|
1112
|
+
|
|
1063
1113
|
router.post('/bridge/agents/:id/browser-config/test', async (c) => {
|
|
1064
1114
|
const agentId = c.req.param('id');
|
|
1065
1115
|
const managed = lifecycle.getAgent(agentId);
|
|
@@ -26,10 +26,14 @@ export interface SystemCapabilities {
|
|
|
26
26
|
hasVirtualCamera: boolean;
|
|
27
27
|
/** Can run headed browser (display + browser) */
|
|
28
28
|
canRunHeadedBrowser: boolean;
|
|
29
|
-
/** Can join video calls (display + browser + audio) */
|
|
29
|
+
/** Can join video calls (display + browser + audio) — may be observer-only on containers */
|
|
30
30
|
canJoinMeetings: boolean;
|
|
31
|
+
/** Can join with full media (real audio/video, not container fake media) */
|
|
32
|
+
canJoinMeetingsFullMedia: boolean;
|
|
31
33
|
/** Can record meetings (display + browser + audio + ffmpeg) */
|
|
32
34
|
canRecordMeetings: boolean;
|
|
35
|
+
/** Container deployment with Xvfb+PulseAudio but fake media (observer-only) */
|
|
36
|
+
isContainerWithFakeMedia: boolean;
|
|
33
37
|
/** ffmpeg available */
|
|
34
38
|
hasFfmpeg: boolean;
|
|
35
39
|
/** Persistent filesystem (not ephemeral container) */
|
|
@@ -187,6 +191,9 @@ export function detectCapabilities(): SystemCapabilities {
|
|
|
187
191
|
if (commandExists('nvidia-smi')) { execSync('nvidia-smi', { timeout: 3000 }); hasGpu = true; }
|
|
188
192
|
} catch { /* no GPU */ }
|
|
189
193
|
|
|
194
|
+
// On containers with fake media, meetings work but only as observer (no real audio/video)
|
|
195
|
+
const isContainerWithFakeMedia = deployment === 'container' && hasBrowser && hasDisplay && hasAudio;
|
|
196
|
+
|
|
190
197
|
const caps: SystemCapabilities = {
|
|
191
198
|
deployment,
|
|
192
199
|
hasBrowser,
|
|
@@ -196,7 +203,9 @@ export function detectCapabilities(): SystemCapabilities {
|
|
|
196
203
|
hasVirtualCamera,
|
|
197
204
|
canRunHeadedBrowser: hasBrowser && hasDisplay,
|
|
198
205
|
canJoinMeetings: hasBrowser && hasDisplay && hasAudio,
|
|
206
|
+
canJoinMeetingsFullMedia: hasBrowser && hasDisplay && hasAudio && !isContainerWithFakeMedia,
|
|
199
207
|
canRecordMeetings: hasBrowser && hasDisplay && hasAudio && hasFfmpeg,
|
|
208
|
+
isContainerWithFakeMedia,
|
|
200
209
|
hasFfmpeg,
|
|
201
210
|
hasPersistentDisk: !!hasPersistentDisk,
|
|
202
211
|
hasGpu,
|
|
@@ -243,7 +252,8 @@ export function getCapabilitySummary(caps?: SystemCapabilities): {
|
|
|
243
252
|
if (c.canRunHeadedBrowser) available.push('Browser (headed/visible)');
|
|
244
253
|
else if (c.hasBrowser) unavailable.push('Headed browser — no display server (install Xvfb)');
|
|
245
254
|
|
|
246
|
-
if (c.
|
|
255
|
+
if (c.canJoinMeetingsFullMedia) available.push('Video meetings — full media (Google Meet, Zoom, Teams)');
|
|
256
|
+
else if (c.canJoinMeetings && c.isContainerWithFakeMedia) available.push('Video meetings — observer only (container: no real audio/video, can read chat + take notes)');
|
|
247
257
|
else unavailable.push('Video meetings — requires display + browser + audio');
|
|
248
258
|
|
|
249
259
|
if (c.canRecordMeetings) available.push('Meeting recording');
|