ft-scout 8.0.3 → 8.0.5

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.
Files changed (48) hide show
  1. package/.firebase/hosting.d2Vi.cache +6 -6
  2. package/README.md +29 -13
  3. package/bin/src/commands/agent.d.ts.map +1 -1
  4. package/bin/src/commands/agent.js +48 -18
  5. package/bin/src/commands/agent.js.map +1 -1
  6. package/bin/src/commands/audit.d.ts.map +1 -1
  7. package/bin/src/commands/audit.js +8 -2
  8. package/bin/src/commands/audit.js.map +1 -1
  9. package/bin/src/commands/dashboard.d.ts.map +1 -1
  10. package/bin/src/commands/dashboard.js +9 -7
  11. package/bin/src/commands/dashboard.js.map +1 -1
  12. package/bin/src/commands/plan.d.ts.map +1 -0
  13. package/bin/src/commands/plan.js +70 -0
  14. package/bin/src/commands/plan.js.map +1 -0
  15. package/bin/src/commands/quota.d.ts.map +1 -1
  16. package/bin/src/commands/quota.js +4 -20
  17. package/bin/src/commands/quota.js.map +1 -1
  18. package/bin/src/commands/risky.d.ts.map +1 -1
  19. package/bin/src/commands/risky.js +8 -2
  20. package/bin/src/commands/risky.js.map +1 -1
  21. package/bin/src/engine/agentEngine.d.ts.map +1 -1
  22. package/bin/src/engine/agentEngine.js +16 -4
  23. package/bin/src/engine/agentEngine.js.map +1 -1
  24. package/bin/src/engine/appControl.d.ts.map +1 -1
  25. package/bin/src/engine/appControl.js +287 -17
  26. package/bin/src/engine/appControl.js.map +1 -1
  27. package/bin/src/engine/liveScreenEngine.d.ts.map +1 -0
  28. package/bin/src/engine/liveScreenEngine.js +390 -0
  29. package/bin/src/engine/liveScreenEngine.js.map +1 -0
  30. package/bin/src/engine/voiceEngine.d.ts.map +1 -1
  31. package/bin/src/engine/voiceEngine.js +1 -0
  32. package/bin/src/engine/voiceEngine.js.map +1 -1
  33. package/bin/src/index.js +18 -7
  34. package/bin/src/index.js.map +1 -1
  35. package/bin/src/server/server.d.ts.map +1 -1
  36. package/bin/src/server/server.js +57 -0
  37. package/bin/src/server/server.js.map +1 -1
  38. package/bin/src/utils/auth.d.ts.map +1 -1
  39. package/bin/src/utils/auth.js +382 -7
  40. package/bin/src/utils/auth.js.map +1 -1
  41. package/bin/src/utils/branding.js +1 -1
  42. package/bin/src/utils/payment.d.ts.map +1 -0
  43. package/bin/src/utils/payment.js +251 -0
  44. package/bin/src/utils/payment.js.map +1 -0
  45. package/firebase.json +20 -6
  46. package/package.json +1 -1
  47. package/web/app.js +97 -16
  48. package/web/styles.css +96 -1
@@ -0,0 +1,251 @@
1
+ import crypto from 'crypto';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import dotenv from 'dotenv';
5
+ import { loadAuthConfig, saveAuthConfig } from './auth.js';
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = path.dirname(__filename);
8
+ dotenv.config({ path: path.resolve(__dirname, '../../../.env'), quiet: true });
9
+ dotenv.config({ quiet: true });
10
+ export const PLAN_CONFIGS = {
11
+ free: {
12
+ id: 'free',
13
+ name: 'Free Community',
14
+ monthlyPrice: 0,
15
+ annualPrice: 0,
16
+ credits: 100,
17
+ description: 'Get started with codebase onboarding',
18
+ features: ['100 AI credits / month', 'Standard response time', '1 CLI session slot', 'Community support'],
19
+ },
20
+ pro: {
21
+ id: 'pro',
22
+ name: 'Pro Developer',
23
+ monthlyPrice: 19,
24
+ annualPrice: 15,
25
+ credits: 1000,
26
+ description: 'For active engineers navigating complex codebases',
27
+ features: ['1,000 AI credits / month', 'Fast-track reasoning engine', '5 concurrent CLI session slots', 'Priority support response'],
28
+ },
29
+ enterprise: {
30
+ id: 'enterprise',
31
+ name: 'Team & Enterprise',
32
+ monthlyPrice: 49,
33
+ annualPrice: 39,
34
+ credits: 10000,
35
+ description: 'Full organization control and dedicated scale',
36
+ features: ['10,000 AI credits / month', 'Continuous in-memory screen streaming HUD', 'Unlimited CLI session connections', 'Dedicated account SLA support'],
37
+ },
38
+ };
39
+ export function getPayPalBaseUrl() {
40
+ return process.env.PAYPAL_MODE === 'live'
41
+ ? 'https://api-m.paypal.com'
42
+ : 'https://api-m.sandbox.paypal.com';
43
+ }
44
+ export async function getPayPalAccessToken() {
45
+ const clientId = process.env.PAYPAL_CLIENT_ID;
46
+ const clientSecret = process.env.PAYPAL_CLIENT_SECRET;
47
+ if (!clientId || !clientSecret) {
48
+ throw new Error('PAYPAL_CLIENT_ID and PAYPAL_CLIENT_SECRET must be configured in environment variables.');
49
+ }
50
+ const auth = Buffer.from(`${clientId}:${clientSecret}`).toString('base64');
51
+ const baseUrl = getPayPalBaseUrl();
52
+ const response = await fetch(`${baseUrl}/v1/oauth2/token`, {
53
+ method: 'POST',
54
+ headers: {
55
+ 'Authorization': `Basic ${auth}`,
56
+ 'Content-Type': 'application/x-www-form-urlencoded',
57
+ },
58
+ body: 'grant_type=client_credentials',
59
+ });
60
+ const data = await response.json();
61
+ if (!response.ok || !data.access_token) {
62
+ throw new Error(`PayPal OAuth failed: ${data.error_description || data.error || 'Unable to retrieve access token'}`);
63
+ }
64
+ return data.access_token;
65
+ }
66
+ export function getPaymentProvider() {
67
+ return 'paypal';
68
+ }
69
+ export function getPublicPaymentConfig() {
70
+ const isLive = process.env.PAYPAL_MODE === 'live';
71
+ const hasPayPalKeys = Boolean(process.env.PAYPAL_CLIENT_ID && process.env.PAYPAL_CLIENT_SECRET);
72
+ return {
73
+ provider: 'paypal',
74
+ isSandbox: !isLive || !hasPayPalKeys,
75
+ paypalClientId: process.env.PAYPAL_CLIENT_ID || undefined,
76
+ plans: PLAN_CONFIGS,
77
+ };
78
+ }
79
+ /**
80
+ * Creates a checkout order strictly with PayPal.
81
+ */
82
+ export async function createCheckoutSession(params) {
83
+ const tier = params.tier;
84
+ if (tier === 'free') {
85
+ throw new Error('Cannot create a payment session for the free tier.');
86
+ }
87
+ const plan = PLAN_CONFIGS[tier];
88
+ if (!plan) {
89
+ throw new Error(`Unknown tier: ${tier}`);
90
+ }
91
+ const billingCycle = params.billingCycle === 'annual' ? 'annual' : 'monthly';
92
+ const unitPrice = billingCycle === 'annual' ? plan.annualPrice * 12 : plan.monthlyPrice;
93
+ const DEFAULT_PLANS_URL = process.env.FRONTTERRAIN_DASHBOARD_URL
94
+ ? `${process.env.FRONTTERRAIN_DASHBOARD_URL.replace(/\/$/, '')}/plans`
95
+ : 'https://scout.frontterrain.com/plans';
96
+ const accessToken = await getPayPalAccessToken();
97
+ const baseUrl = getPayPalBaseUrl();
98
+ const returnUrl = params.returnUrl || DEFAULT_PLANS_URL;
99
+ const successReturnUrl = `${returnUrl}${returnUrl.includes('?') ? '&' : '?'}success=true&tier=${tier}`;
100
+ const cancelReturnUrl = params.cancelUrl || `${returnUrl}${returnUrl.includes('?') ? '&' : '?'}canceled=true`;
101
+ const orderPayload = {
102
+ intent: 'CAPTURE',
103
+ purchase_units: [
104
+ {
105
+ reference_id: `scout_${tier}_${Date.now()}`,
106
+ description: `FrontTerrain Scout ${plan.name} (${billingCycle})`,
107
+ amount: {
108
+ currency_code: 'USD',
109
+ value: unitPrice.toFixed(2),
110
+ },
111
+ },
112
+ ],
113
+ application_context: {
114
+ brand_name: 'FrontTerrain Scout',
115
+ landing_page: 'BILLING',
116
+ user_action: 'PAY_NOW',
117
+ return_url: successReturnUrl,
118
+ cancel_url: cancelReturnUrl,
119
+ },
120
+ };
121
+ const orderRes = await fetch(`${baseUrl}/v2/checkout/orders`, {
122
+ method: 'POST',
123
+ headers: {
124
+ 'Authorization': `Bearer ${accessToken}`,
125
+ 'Content-Type': 'application/json',
126
+ },
127
+ body: JSON.stringify(orderPayload),
128
+ });
129
+ const orderData = await orderRes.json();
130
+ if (!orderRes.ok || !orderData.id) {
131
+ throw new Error(`PayPal Order Creation Failed: ${orderData.message || JSON.stringify(orderData)}`);
132
+ }
133
+ const approveLink = orderData.links?.find((l) => l.rel === 'approve')?.href;
134
+ if (!approveLink) {
135
+ throw new Error('PayPal did not return an approval URL for this order.');
136
+ }
137
+ return {
138
+ success: true,
139
+ provider: 'paypal',
140
+ sessionId: orderData.id,
141
+ orderId: orderData.id,
142
+ checkoutUrl: approveLink,
143
+ amount: unitPrice,
144
+ currency: 'USD',
145
+ tier,
146
+ billingCycle,
147
+ isSandbox: process.env.PAYPAL_MODE !== 'live',
148
+ };
149
+ }
150
+ /**
151
+ * Strictly verifies the PayPal transaction using PayPal REST API.
152
+ * Validates order status, currency, and paid amount against the requested plan.
153
+ * Absolutely no simulation or fake tokens allowed.
154
+ */
155
+ export async function verifyAndApplyUpgrade(params) {
156
+ const targetOrderId = (params.orderId || params.token || params.sessionId || '').trim();
157
+ const tier = params.tier;
158
+ if (!targetOrderId) {
159
+ throw new Error('PayPal payment verification failed: Missing order ID or payment token.');
160
+ }
161
+ const plan = PLAN_CONFIGS[tier];
162
+ if (!plan || tier === 'free') {
163
+ throw new Error(`Invalid plan tier specified for upgrade: ${tier}`);
164
+ }
165
+ const billingCycle = params.billingCycle === 'annual' ? 'annual' : 'monthly';
166
+ const expectedPrice = billingCycle === 'annual' ? plan.annualPrice * 12 : plan.monthlyPrice;
167
+ // Strict verification against real PayPal API
168
+ const accessToken = await getPayPalAccessToken();
169
+ const baseUrl = getPayPalBaseUrl();
170
+ let orderData = null;
171
+ let transactionId = '';
172
+ // Step 1: Attempt to capture the PayPal order
173
+ const captureRes = await fetch(`${baseUrl}/v2/checkout/orders/${encodeURIComponent(targetOrderId)}/capture`, {
174
+ method: 'POST',
175
+ headers: {
176
+ 'Authorization': `Bearer ${accessToken}`,
177
+ 'Content-Type': 'application/json',
178
+ },
179
+ });
180
+ const captureJson = await captureRes.json();
181
+ if (captureRes.ok && (captureJson.status === 'COMPLETED' || captureJson.status === 'SAVED')) {
182
+ orderData = captureJson;
183
+ const captureUnit = captureJson.purchase_units?.[0]?.payments?.captures?.[0];
184
+ transactionId = captureUnit?.id || captureJson.id;
185
+ }
186
+ else if (captureJson.status === 'COMPLETED' ||
187
+ captureJson.name === 'UNPROCESSABLE_ENTITY' ||
188
+ captureJson.details?.some((d) => d.issue === 'ORDER_ALREADY_CAPTURED')) {
189
+ // Step 2: If order was already captured, fetch order details to verify
190
+ const getRes = await fetch(`${baseUrl}/v2/checkout/orders/${encodeURIComponent(targetOrderId)}`, {
191
+ method: 'GET',
192
+ headers: {
193
+ 'Authorization': `Bearer ${accessToken}`,
194
+ 'Content-Type': 'application/json',
195
+ },
196
+ });
197
+ const getJson = await getRes.json();
198
+ if (getRes.ok && getJson.status === 'COMPLETED') {
199
+ orderData = getJson;
200
+ const captureUnit = getJson.purchase_units?.[0]?.payments?.captures?.[0];
201
+ transactionId = captureUnit?.id || getJson.id;
202
+ }
203
+ else {
204
+ throw new Error(`PayPal Order Verification Failed: Order status is "${getJson.status || 'UNKNOWN'}" (must be COMPLETED).`);
205
+ }
206
+ }
207
+ else {
208
+ const errorMsg = captureJson.message || captureJson.details?.[0]?.description || JSON.stringify(captureJson);
209
+ throw new Error(`PayPal capture failed: ${errorMsg}`);
210
+ }
211
+ // Step 3: Strict verification of amount, currency, and purchase details
212
+ if (!orderData || orderData.status !== 'COMPLETED') {
213
+ throw new Error(`PayPal payment verification rejected: Order status is ${orderData?.status || 'INCOMPLETE'}.`);
214
+ }
215
+ const purchaseUnit = orderData.purchase_units?.[0];
216
+ const captureInfo = purchaseUnit?.payments?.captures?.[0];
217
+ const paidCurrency = captureInfo?.amount?.currency_code || purchaseUnit?.amount?.currency_code;
218
+ const paidAmount = parseFloat(captureInfo?.amount?.value || purchaseUnit?.amount?.value || '0');
219
+ if (paidCurrency !== 'USD') {
220
+ throw new Error(`PayPal payment verification rejected: Expected currency USD, received ${paidCurrency}.`);
221
+ }
222
+ // Allow a small 0.01 tolerance for floating point representations
223
+ if (paidAmount < expectedPrice - 0.01) {
224
+ throw new Error(`PayPal payment verification rejected: Paid amount ($${paidAmount.toFixed(2)}) does not match required plan price ($${expectedPrice.toFixed(2)}).`);
225
+ }
226
+ // Step 4: Apply verified upgrade to local auth config
227
+ const config = loadAuthConfig();
228
+ const now = new Date();
229
+ const renewalDate = new Date(now);
230
+ renewalDate.setMonth(renewalDate.getMonth() + (billingCycle === 'annual' ? 12 : 1));
231
+ const updatedConfig = {
232
+ ...config,
233
+ currentTier: tier,
234
+ creditsTotal: plan.credits,
235
+ creditsUsed: 0, // Reset used credits upon verified purchase
236
+ resetCredits: true,
237
+ transactionId,
238
+ renewAt: renewalDate.toISOString(),
239
+ };
240
+ saveAuthConfig(updatedConfig);
241
+ return {
242
+ success: true,
243
+ tier,
244
+ creditsAdded: plan.credits,
245
+ totalCredits: plan.credits,
246
+ renewAt: renewalDate.toISOString(),
247
+ auth: updatedConfig,
248
+ transactionId: transactionId || targetOrderId,
249
+ };
250
+ }
251
+ //# sourceMappingURL=payment.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"payment.js","sourceRoot":"","sources":["payment.ts"],"names":[],"mappings":"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"}
package/firebase.json CHANGED
@@ -7,23 +7,37 @@
7
7
  "**/.*",
8
8
  "**/node_modules/**"
9
9
  ],
10
+ "cleanUrls": true,
11
+ "trailingSlash": false,
10
12
  "rewrites": [
11
13
  {
12
- "source": "**",
13
- "destination": "/index.html"
14
+ "source": "/auth",
15
+ "destination": "/authentication.html"
14
16
  },
15
17
  {
16
- "source": "auth",
18
+ "source": "/auth/**",
17
19
  "destination": "/authentication.html"
18
20
  },
19
21
  {
20
- "source": "plans",
22
+ "source": "/plans",
21
23
  "destination": "/paid_plans.html"
22
24
  },
23
25
  {
24
- "source": "cli",
26
+ "source": "/plans/**",
27
+ "destination": "/paid_plans.html"
28
+ },
29
+ {
30
+ "source": "/cli",
25
31
  "destination": "/web_cli.html"
26
- }
32
+ },
33
+ {
34
+ "source": "/cli/**",
35
+ "destination": "/web_cli.html"
36
+ },
37
+ {
38
+ "source": "**",
39
+ "destination": "/index.html"
40
+ }
27
41
  ]
28
42
  }
29
43
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ft-scout",
3
- "version": "8.0.3",
3
+ "version": "8.0.5",
4
4
  "type": "module",
5
5
  "description": "FrontTerrain — The Onboarding Co-Pilot for New Codebases",
6
6
  "main": "index.js",
package/web/app.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /* ==========================================================================
2
- FrontTerrain Scout — Multi-Page Application Engine v8.0.3
2
+ FrontTerrain Scout — Multi-Page Application Engine v8.0.5
3
3
  ========================================================================== */
4
4
 
5
5
  const FIREBASE_CONFIG = {
@@ -105,6 +105,50 @@ function highlightActiveNav() {
105
105
  });
106
106
  }
107
107
 
108
+ // Check for payment redirect callback from PayPal / external gateways
109
+ async function checkPaymentRedirectCallback() {
110
+ const params = new URLSearchParams(window.location.search);
111
+ const orderId = params.get('token') || params.get('session_id') || params.get('orderId');
112
+ const isSuccess = params.get('success') === 'true' || Boolean(params.get('token'));
113
+ const tier = params.get('tier') || 'pro';
114
+
115
+ if (orderId && isSuccess) {
116
+ window.history.replaceState({}, document.title, window.location.pathname);
117
+ showToast('Verifying payment with PayPal...', 'info');
118
+
119
+ try {
120
+ const res = await fetch('/api/payment/verify', {
121
+ method: 'POST',
122
+ headers: { 'Content-Type': 'application/json' },
123
+ body: JSON.stringify({
124
+ orderId,
125
+ token: orderId,
126
+ sessionId: orderId,
127
+ tier,
128
+ }),
129
+ });
130
+ const data = await res.json();
131
+ if (data.success) {
132
+ state.currentTier = data.tier;
133
+ state.creditsTotal = data.totalCredits;
134
+ state.creditsUsed = 0;
135
+ state.renewAt = data.renewAt;
136
+ saveSessionToStorage();
137
+ renderAllDynamicComponents();
138
+ const tierName = (TIER_DETAILS[data.tier] || {}).name || data.tier;
139
+ showToast(`🎉 PayPal Payment Verified! Welcome to ${tierName}. Your ${data.totalCredits.toLocaleString()} monthly credits are active!`, 'success');
140
+ } else {
141
+ showToast(data.error || 'PayPal verification failed.', 'error');
142
+ }
143
+ } catch (err) {
144
+ showToast('Payment verification network error.', 'error');
145
+ }
146
+ } else if (params.get('canceled') === 'true') {
147
+ window.history.replaceState({}, document.title, window.location.pathname);
148
+ showToast('Subscription checkout was canceled.', 'warning');
149
+ }
150
+ }
151
+
108
152
  // Initialize Application
109
153
  document.addEventListener('DOMContentLoaded', async () => {
110
154
  highlightActiveNav();
@@ -115,6 +159,7 @@ document.addEventListener('DOMContentLoaded', async () => {
115
159
  initTerminalSimulator();
116
160
  renderAllDynamicComponents();
117
161
  await checkServerStatusAndSession();
162
+ await checkPaymentRedirectCallback();
118
163
 
119
164
  if (getEl('pacman-canvas')) {
120
165
  initPacmanGame();
@@ -258,26 +303,53 @@ function initEventListeners() {
258
303
  getEl('btn-close-checkout')?.addEventListener('click', closeCheckoutModal);
259
304
  getEl('btn-cancel-checkout')?.addEventListener('click', closeCheckoutModal);
260
305
 
261
- getEl('btn-confirm-checkout')?.addEventListener('click', () => {
306
+ getEl('btn-confirm-checkout')?.addEventListener('click', async () => {
262
307
  if (!pendingCheckoutTier) return;
263
308
 
264
309
  const targetTier = pendingCheckoutTier;
265
310
  const tierInfo = TIER_DETAILS[targetTier];
266
311
  if (!tierInfo) return;
267
312
 
268
- state.currentTier = targetTier;
269
- state.creditsTotal = targetTier === 'pro' ? 1000 : targetTier === 'enterprise' ? 10000 : 100;
313
+ const btn = getEl('btn-confirm-checkout');
314
+ const origBtnHtml = btn ? btn.innerHTML : '';
315
+ if (btn) {
316
+ btn.disabled = true;
317
+ btn.innerHTML = '<i class="fa-solid fa-spinner fa-spin"></i> Initializing Gateway...';
318
+ }
270
319
 
271
- const now = new Date();
272
- state.renewAt = new Date(now.getFullYear(), now.getMonth() + 1, 1, 0, 0, 0).toISOString();
320
+ try {
321
+ const res = await fetch('/api/payment/create-checkout', {
322
+ method: 'POST',
323
+ headers: { 'Content-Type': 'application/json' },
324
+ body: JSON.stringify({
325
+ tier: targetTier,
326
+ billingCycle: state.billingCycle || 'monthly',
327
+ returnUrl: window.location.origin + '/plans',
328
+ email: state.user?.email,
329
+ }),
330
+ });
273
331
 
274
- saveSessionToStorage();
275
- syncAuthWithServer();
276
- closeCheckoutModal();
277
- renderCreditUsageUI();
278
- renderTiersUI();
279
- updateUserUI();
280
- showToast(`Subscription activated! Welcome to ${tierInfo.name}. Quota upgraded to ${state.creditsTotal.toLocaleString()} credits.`, 'success');
332
+ const data = await res.json();
333
+ if (!res.ok || !data.success) {
334
+ throw new Error(data.error || 'Failed to initialize payment session.');
335
+ }
336
+
337
+ // PayPal Checkout redirect
338
+ if (data.checkoutUrl) {
339
+ if (btn) btn.innerHTML = '<i class="fa-brands fa-paypal"></i> Redirecting to PayPal...';
340
+ window.location.href = data.checkoutUrl;
341
+ return;
342
+ }
343
+
344
+ throw new Error('PayPal checkout URL could not be generated.');
345
+ } catch (err) {
346
+ showToast(err.message || 'Payment processing failed.', 'error');
347
+ } finally {
348
+ if (btn) {
349
+ btn.disabled = false;
350
+ btn.innerHTML = origBtnHtml;
351
+ }
352
+ }
281
353
  });
282
354
 
283
355
  // Password Visibility Toggle Buttons
@@ -436,11 +508,20 @@ function initTerminalSimulator() {
436
508
  response.innerHTML = `
437
509
  <span class="term-yellow">Available Scout Commands:</span><br>
438
510
  ↳ <code>scout agent</code> - Launch autonomous AI co-pilot agent & reasoning loop<br>
511
+ ↳ <code>scout agent --screen-share</code> - Real-time in-memory screen streaming to vision AI with target window isolation<br>
439
512
  ↳ <code>scout status</code> - Inspect CLI authentication, active workspace & credit balance<br>
440
513
  ↳ <code>scout analyze</code> - Run instant AST codebase teardown and entry point indexing<br>
441
514
  ↳ <code>scout teardown</code> - Generate strategic onboarding map & architectural moat report<br>
442
515
  ↳ <code>scout byok</code> - Verify custom LLM proxy endpoint connection<br>
443
516
  ↳ <code>clear</code> - Clear terminal buffer<br>
517
+ `;
518
+ } else if (lower.includes('screen-share') || lower.includes('screenshare') || lower.includes('live') || lower.includes('-s') || lower.includes('/screenshare') || lower.includes('/livescreen')) {
519
+ response.innerHTML = `
520
+ <span class="term-bg-cyan" style="background-color: #00e5ff; color: #08090c; font-weight: bold; padding: 2px 8px; border-radius: 4px;"> SCOUT LIVE SCREEN SHARE &amp; VISION ENGINE </span><br>
521
+ <span class="term-green">✔ Real-time in-memory screen streaming active (0 files written to disk).</span><br>
522
+ <span class="term-yellow">↳ Target App Isolation:</span> Chrome window bounds locked [BoundingRectangle]<br>
523
+ <span class="term-purple" style="color: #38bdf8;">↳ Visual HUD:</span> Continuous sky-blue (#00e5ff) aura HUD overlay active<br>
524
+ <span class="term-cyan">↳ Multimodal Vision:</span> Real-time video frame streaming to Gemini 2.0 Flash / NVIDIA Vision.<br>
444
525
  `;
445
526
  } else if (lower.includes('agent')) {
446
527
  response.innerHTML = `
@@ -448,11 +529,11 @@ function initTerminalSimulator() {
448
529
  <span class="term-green">✔ Agent Orchestrator active (Gemini 2.5 Flash / Deep Reasoning Engine)</span><br>
449
530
  <span class="term-cyan">↳ Workspace Vector Memory:</span> 124 files indexed and loaded<br>
450
531
  <span class="term-yellow">↳ Pair-Programming Status:</span> Ready for interactive tasks & refactoring loops<br>
451
- <span class="term-dim">↳ Try typing 'scout analyze' or 'scout teardown' to execute automated codebase operations.</span><br>
532
+ <span class="term-dim">↳ Tip: Run 'scout agent --screen-share' to stream your active window directly to vision models.</span><br>
452
533
  `;
453
534
  } else if (lower.includes('status')) {
454
535
  response.innerHTML = `
455
- <span class="term-green">✔ Scout CLI Engine: v8.0.3 Online</span><br>
536
+ <span class="term-green">✔ Scout CLI Engine: v8.0.5 Online</span><br>
456
537
  ↳ User: <strong>${escapeHtml(state.user ? state.user.name : 'Guest Session')}</strong><br>
457
538
  ↳ Active Subscription: <strong>${escapeHtml(TIER_DETAILS[state.currentTier].name)}</strong><br>
458
539
  ↳ Credit Quota: <strong>${state.byokEnabled ? 'BYOK Mode (Unlimited)' : `${state.creditsUsed} / ${state.creditsTotal} Credits Used`}</strong><br>
@@ -1284,7 +1365,7 @@ function initPacmanGame() {
1284
1365
  function updateLivesUI() {
1285
1366
  const livesEl = getEl('pacman-lives');
1286
1367
  if (livesEl) {
1287
- livesEl.innerHTML = Array(Math.max(0, lives)).fill('<span class="life-pacman">🟡</span>').join('');
1368
+ livesEl.innerHTML = Array(Math.max(0, lives)).fill('<span class="life-pacman">❤️</span>').join('');
1288
1369
  }
1289
1370
  }
1290
1371
 
package/web/styles.css CHANGED
@@ -1,5 +1,5 @@
1
1
  /* ==========================================================================
2
- FrontTerrain Scout — Design System & Visual Engine v8.0.3
2
+ FrontTerrain Scout — Design System & Visual Engine v8.0.5
3
3
  Inspired by HappyRobot, Linear.app & Vercel aesthetics
4
4
  ========================================================================== */
5
5
 
@@ -1062,6 +1062,101 @@ h1, h2, h3, h4, h5, h6 {
1062
1062
  .footer-links a { color: var(--text-secondary); text-decoration: none; font-size: 12px; transition: var(--transition-fast); }
1063
1063
  .footer-links a:hover { color: var(--hr-rust); }
1064
1064
 
1065
+ /* ==========================================================================
1066
+ Visual Perception & Live Screen Sharing Engine v8.0.5
1067
+ ========================================================================== */
1068
+ .vision-banner {
1069
+ margin-bottom: 28px;
1070
+ position: relative;
1071
+ overflow: hidden;
1072
+ border: 1px solid rgba(0, 229, 255, 0.28);
1073
+ box-shadow: 0 8px 32px -8px rgba(0, 229, 255, 0.15);
1074
+ background: radial-gradient(ellipse at 90% 10%, rgba(0, 229, 255, 0.08) 0%, rgba(21, 24, 33, 0.85) 60%);
1075
+ }
1076
+
1077
+ .vision-banner::before {
1078
+ content: '';
1079
+ position: absolute;
1080
+ top: 0;
1081
+ left: 0;
1082
+ right: 0;
1083
+ height: 2px;
1084
+ background: linear-gradient(90deg, transparent, #00e5ff, #38bdf8, transparent);
1085
+ animation: auraScanline 3s ease-in-out infinite;
1086
+ }
1087
+
1088
+ @keyframes auraScanline {
1089
+ 0%, 100% { opacity: 0.4; }
1090
+ 50% { opacity: 1; }
1091
+ }
1092
+
1093
+ .vision-grid {
1094
+ display: grid;
1095
+ grid-template-columns: repeat(3, 1fr);
1096
+ gap: 16px;
1097
+ margin-top: 20px;
1098
+ }
1099
+
1100
+ @media (max-width: 860px) {
1101
+ .vision-grid {
1102
+ grid-template-columns: 1fr;
1103
+ }
1104
+ }
1105
+
1106
+ .vision-feature-pill {
1107
+ background: rgba(16, 18, 24, 0.6);
1108
+ border: 1px solid rgba(255, 255, 255, 0.07);
1109
+ border-radius: var(--radius-md);
1110
+ padding: 16px;
1111
+ transition: var(--transition-normal);
1112
+ position: relative;
1113
+ }
1114
+
1115
+ .vision-feature-pill:hover {
1116
+ border-color: rgba(0, 229, 255, 0.4);
1117
+ background: rgba(28, 32, 45, 0.7);
1118
+ transform: translateY(-2px);
1119
+ }
1120
+
1121
+ .vision-icon-aura {
1122
+ width: 40px;
1123
+ height: 40px;
1124
+ border-radius: 10px;
1125
+ background: rgba(0, 229, 255, 0.12);
1126
+ color: #00e5ff;
1127
+ display: flex;
1128
+ align-items: center;
1129
+ justify-content: center;
1130
+ font-size: 18px;
1131
+ margin-bottom: 12px;
1132
+ border: 1px solid rgba(0, 229, 255, 0.3);
1133
+ box-shadow: 0 0 14px rgba(0, 229, 255, 0.25);
1134
+ }
1135
+
1136
+ .vision-badge-live {
1137
+ display: inline-flex;
1138
+ align-items: center;
1139
+ gap: 6px;
1140
+ font-size: 11px;
1141
+ font-weight: 700;
1142
+ text-transform: uppercase;
1143
+ letter-spacing: 0.08em;
1144
+ padding: 3px 10px;
1145
+ border-radius: 9999px;
1146
+ background: rgba(0, 229, 255, 0.15);
1147
+ color: #00e5ff;
1148
+ border: 1px solid rgba(0, 229, 255, 0.4);
1149
+ }
1150
+
1151
+ .aura-dot-pulse {
1152
+ width: 7px;
1153
+ height: 7px;
1154
+ background-color: #00e5ff;
1155
+ border-radius: 50%;
1156
+ box-shadow: 0 0 8px #00e5ff;
1157
+ animation: blink 1.2s ease-in-out infinite;
1158
+ }
1159
+
1065
1160
  /* ==========================================================================
1066
1161
  Mobile & Responsive Layout Media Queries
1067
1162
  ========================================================================== */