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.
- package/.firebase/hosting.d2Vi.cache +6 -6
- package/README.md +29 -13
- package/bin/src/commands/agent.d.ts.map +1 -1
- package/bin/src/commands/agent.js +48 -18
- package/bin/src/commands/agent.js.map +1 -1
- package/bin/src/commands/audit.d.ts.map +1 -1
- package/bin/src/commands/audit.js +8 -2
- package/bin/src/commands/audit.js.map +1 -1
- package/bin/src/commands/dashboard.d.ts.map +1 -1
- package/bin/src/commands/dashboard.js +9 -7
- package/bin/src/commands/dashboard.js.map +1 -1
- package/bin/src/commands/plan.d.ts.map +1 -0
- package/bin/src/commands/plan.js +70 -0
- package/bin/src/commands/plan.js.map +1 -0
- package/bin/src/commands/quota.d.ts.map +1 -1
- package/bin/src/commands/quota.js +4 -20
- package/bin/src/commands/quota.js.map +1 -1
- package/bin/src/commands/risky.d.ts.map +1 -1
- package/bin/src/commands/risky.js +8 -2
- package/bin/src/commands/risky.js.map +1 -1
- package/bin/src/engine/agentEngine.d.ts.map +1 -1
- package/bin/src/engine/agentEngine.js +16 -4
- package/bin/src/engine/agentEngine.js.map +1 -1
- package/bin/src/engine/appControl.d.ts.map +1 -1
- package/bin/src/engine/appControl.js +287 -17
- package/bin/src/engine/appControl.js.map +1 -1
- package/bin/src/engine/liveScreenEngine.d.ts.map +1 -0
- package/bin/src/engine/liveScreenEngine.js +390 -0
- package/bin/src/engine/liveScreenEngine.js.map +1 -0
- package/bin/src/engine/voiceEngine.d.ts.map +1 -1
- package/bin/src/engine/voiceEngine.js +1 -0
- package/bin/src/engine/voiceEngine.js.map +1 -1
- package/bin/src/index.js +18 -7
- package/bin/src/index.js.map +1 -1
- package/bin/src/server/server.d.ts.map +1 -1
- package/bin/src/server/server.js +57 -0
- package/bin/src/server/server.js.map +1 -1
- package/bin/src/utils/auth.d.ts.map +1 -1
- package/bin/src/utils/auth.js +382 -7
- package/bin/src/utils/auth.js.map +1 -1
- package/bin/src/utils/branding.js +1 -1
- package/bin/src/utils/payment.d.ts.map +1 -0
- package/bin/src/utils/payment.js +251 -0
- package/bin/src/utils/payment.js.map +1 -0
- package/firebase.json +20 -6
- package/package.json +1 -1
- package/web/app.js +97 -16
- 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": "/
|
|
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": "
|
|
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
package/web/app.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/* ==========================================================================
|
|
2
|
-
FrontTerrain Scout — Multi-Page Application Engine v8.0.
|
|
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
|
-
|
|
269
|
-
|
|
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
|
-
|
|
272
|
-
|
|
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
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
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 & 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">↳
|
|
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.
|
|
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"
|
|
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.
|
|
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
|
========================================================================== */
|