yap2app 1.1.3 → 1.1.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/bin/cli.bundle.js +503 -486
- package/bin/cli.js +503 -486
- package/bridge.js +147 -18
- package/package.json +1 -1
- package/polyfills.js +48 -5
package/bridge.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import http from 'http';
|
|
2
|
+
import https from 'https';
|
|
2
3
|
import fsSync from 'fs';
|
|
3
4
|
import path from 'path';
|
|
4
5
|
import util from 'util';
|
|
@@ -12,18 +13,61 @@ const fs = fsSync.promises || {
|
|
|
12
13
|
stat: util.promisify(fsSync.stat)
|
|
13
14
|
};
|
|
14
15
|
|
|
15
|
-
const PORT = process.env.YAP2APP_PORT || 10420;
|
|
16
|
+
const PORT = Number(process.env.YAP2APP_PORT) || 10420;
|
|
16
17
|
const PROJECT_ROOT = process.cwd();
|
|
18
|
+
const CLOUD_RELAY_URL = process.env.YAP2APP_CLOUD_URL || 'https://yap2app-dot-cybage-hackathon.uc.r.appspot.com';
|
|
19
|
+
const PAIR_CODE = (process.env.YAP2APP_PAIR_CODE || ('CYB-' + Math.floor(100 + Math.random() * 900))).toUpperCase();
|
|
20
|
+
|
|
21
|
+
// Helper to send outbound HTTPS requests to the Yap2App Cloud Relay
|
|
22
|
+
function sendCloudRelayRequest(method, endpoint, payload) {
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
try {
|
|
25
|
+
const url = new URL(`${CLOUD_RELAY_URL}${endpoint}`);
|
|
26
|
+
const body = payload ? JSON.stringify(payload) : '';
|
|
27
|
+
const req = https.request({
|
|
28
|
+
hostname: url.hostname,
|
|
29
|
+
port: 443,
|
|
30
|
+
path: url.pathname + url.search,
|
|
31
|
+
method: method,
|
|
32
|
+
headers: {
|
|
33
|
+
'Content-Type': 'application/json',
|
|
34
|
+
'Content-Length': Buffer.byteLength(body),
|
|
35
|
+
'User-Agent': 'yap2app-cli/1.1.5'
|
|
36
|
+
},
|
|
37
|
+
timeout: 6000
|
|
38
|
+
}, (res) => {
|
|
39
|
+
let respData = '';
|
|
40
|
+
res.on('data', chunk => respData += chunk);
|
|
41
|
+
res.on('end', () => {
|
|
42
|
+
try {
|
|
43
|
+
resolve({ ok: res.statusCode >= 200 && res.statusCode < 300, status: res.statusCode, data: JSON.parse(respData || '{}') });
|
|
44
|
+
} catch {
|
|
45
|
+
resolve({ ok: false, status: res.statusCode, data: null });
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
req.on('error', () => resolve({ ok: false, status: 0, data: null }));
|
|
50
|
+
req.on('timeout', () => { req.destroy(); resolve({ ok: false, status: 0, data: null }); });
|
|
51
|
+
if (body) req.write(body);
|
|
52
|
+
req.end();
|
|
53
|
+
} catch {
|
|
54
|
+
resolve({ ok: false, status: 0, data: null });
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
}
|
|
17
58
|
|
|
18
59
|
// Bulletproof W3C Private Network Access (PNA) and Dynamic Origin CORS Headers
|
|
19
60
|
function getCorsHeaders(req) {
|
|
20
61
|
const origin = req?.headers?.origin || '*';
|
|
62
|
+
const requestedHeaders = req?.headers?.['access-control-request-headers'] || '*';
|
|
21
63
|
return {
|
|
22
64
|
'Access-Control-Allow-Origin': origin,
|
|
23
|
-
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, PUT, DELETE, HEAD',
|
|
24
|
-
'Access-Control-Allow-Headers':
|
|
65
|
+
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS, PUT, DELETE, HEAD, PATCH',
|
|
66
|
+
'Access-Control-Allow-Headers': requestedHeaders === '*' ? 'Content-Type, Authorization, Access-Control-Request-Private-Network, X-Requested-With, Accept, Origin, *' : requestedHeaders,
|
|
25
67
|
'Access-Control-Allow-Private-Network': 'true',
|
|
68
|
+
'Access-Control-Allow-Credentials': 'true',
|
|
26
69
|
'Access-Control-Max-Age': '86400',
|
|
70
|
+
'Vary': 'Origin, Access-Control-Request-Headers, Access-Control-Request-Private-Network',
|
|
27
71
|
'Content-Type': 'application/json'
|
|
28
72
|
};
|
|
29
73
|
}
|
|
@@ -83,25 +127,42 @@ const server = http.createServer(async (req, res) => {
|
|
|
83
127
|
return;
|
|
84
128
|
}
|
|
85
129
|
|
|
86
|
-
|
|
87
|
-
|
|
130
|
+
let pathname = '/';
|
|
131
|
+
let searchParams = new URLSearchParams();
|
|
132
|
+
try {
|
|
133
|
+
const parsedUrl = new URL(req.url, `http://${req.headers.host || '127.0.0.1:10420'}`);
|
|
134
|
+
pathname = parsedUrl.pathname.replace(/\/+$/, '') || '/';
|
|
135
|
+
searchParams = parsedUrl.searchParams;
|
|
136
|
+
} catch {
|
|
137
|
+
pathname = (req.url || '/').split('?')[0].replace(/\/+$/, '') || '/';
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// 2. Health & Root Info (fast ping endpoints)
|
|
141
|
+
if (req.method === 'GET' && (
|
|
142
|
+
pathname === '/' ||
|
|
143
|
+
pathname === '/health' ||
|
|
144
|
+
pathname === '/api/health' ||
|
|
145
|
+
pathname === '/ping' ||
|
|
146
|
+
pathname === '/api/ping'
|
|
147
|
+
)) {
|
|
88
148
|
res.writeHead(200, headers);
|
|
89
149
|
res.end(JSON.stringify({
|
|
90
150
|
status: 'healthy',
|
|
91
151
|
service: 'Yap2App-Localhost-Bridge',
|
|
92
|
-
version: '1.
|
|
152
|
+
version: '1.1.5',
|
|
93
153
|
port: PORT,
|
|
154
|
+
pair_code: PAIR_CODE,
|
|
94
155
|
project_root: PROJECT_ROOT,
|
|
95
|
-
project_name: path.basename(PROJECT_ROOT)
|
|
156
|
+
project_name: path.basename(PROJECT_ROOT),
|
|
157
|
+
ready: true
|
|
96
158
|
}));
|
|
97
159
|
return;
|
|
98
160
|
}
|
|
99
161
|
|
|
100
162
|
// 3. GET /api/context (Shallow Codebase Scanner with Optional Prompt Query)
|
|
101
|
-
if (req.method === 'GET' &&
|
|
163
|
+
if (req.method === 'GET' && (pathname === '/api/context' || pathname === '/context')) {
|
|
102
164
|
try {
|
|
103
|
-
const
|
|
104
|
-
const promptQuery = parsedUrl.searchParams.get('q') || '';
|
|
165
|
+
const promptQuery = searchParams.get('q') || '';
|
|
105
166
|
console.log(`[Yap2App Bridge] 🔍 Scanning shallow codebase context in: ${PROJECT_ROOT}${promptQuery ? ` (ranking for query: "${promptQuery}")` : ''}`);
|
|
106
167
|
const context = await scanCodebase(PROJECT_ROOT, promptQuery);
|
|
107
168
|
res.writeHead(200, headers);
|
|
@@ -115,7 +176,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
115
176
|
}
|
|
116
177
|
|
|
117
178
|
// 4. POST /api/diff (Enterprise Diff & Safety Gate Review)
|
|
118
|
-
if (req.method === 'POST' &&
|
|
179
|
+
if (req.method === 'POST' && (pathname === '/api/diff' || pathname === '/diff')) {
|
|
119
180
|
let body = '';
|
|
120
181
|
req.on('data', chunk => body += chunk.toString());
|
|
121
182
|
req.on('end', async () => {
|
|
@@ -159,7 +220,7 @@ const server = http.createServer(async (req, res) => {
|
|
|
159
220
|
}
|
|
160
221
|
|
|
161
222
|
// 5. POST /api/write (Approved Write to Disk)
|
|
162
|
-
if (req.method === 'POST' &&
|
|
223
|
+
if (req.method === 'POST' && (pathname === '/api/write' || pathname === '/write')) {
|
|
163
224
|
let body = '';
|
|
164
225
|
req.on('data', chunk => body += chunk.toString());
|
|
165
226
|
req.on('end', async () => {
|
|
@@ -199,19 +260,85 @@ const server = http.createServer(async (req, res) => {
|
|
|
199
260
|
|
|
200
261
|
// 404 Fallback
|
|
201
262
|
res.writeHead(404, headers);
|
|
202
|
-
res.end(JSON.stringify({ error: 'Endpoint Not Found' }));
|
|
263
|
+
res.end(JSON.stringify({ error: 'Endpoint Not Found', path: pathname }));
|
|
203
264
|
});
|
|
204
265
|
|
|
205
|
-
|
|
206
|
-
|
|
266
|
+
// Gracefully handle EADDRINUSE without crashing
|
|
267
|
+
server.on('error', (err) => {
|
|
268
|
+
if (err && err.code === 'EADDRINUSE') {
|
|
207
269
|
console.log(`\n===============================================================`);
|
|
208
|
-
console.log(
|
|
209
|
-
console.log(`📡 Listening on: http://127.0.0.1:${port}`);
|
|
270
|
+
console.log(`📡 Localhost port ${PORT} is active. Cloud Relay continues seamlessly!`);
|
|
210
271
|
console.log(`📁 Active Workspace: ${PROJECT_ROOT}`);
|
|
211
|
-
console.log(
|
|
272
|
+
console.log(`🔑 Pairing Code: ${PAIR_CODE}`);
|
|
273
|
+
console.log(`🌐 Web Studio: ${CLOUD_RELAY_URL}`);
|
|
274
|
+
console.log(`===============================================================\n`);
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
console.error('[Yap2App Bridge Error]:', err);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
export async function startBridge(port = PORT) {
|
|
281
|
+
server.listen(port, '0.0.0.0', () => {
|
|
282
|
+
console.log(`\n===============================================================`);
|
|
283
|
+
console.log(`🚀 Yap2App Localhost Bridge & Telemetry is ACTIVE!`);
|
|
284
|
+
console.log(`📡 Local Endpoint: http://127.0.0.1:${port}`);
|
|
285
|
+
console.log(`📁 Active Workspace: ${PROJECT_ROOT}`);
|
|
286
|
+
console.log(`🔑 Pairing Code: ${PAIR_CODE}`);
|
|
287
|
+
console.log(`🌐 Connected to Web Studio: ${CLOUD_RELAY_URL}`);
|
|
288
|
+
console.log(`🛡️ Enterprise Diff, W3C PNA & Cloud Relay Enabled`);
|
|
212
289
|
console.log(`===============================================================\n`);
|
|
213
290
|
console.log(`Ready for 1-click sync from Yap2App Web Studio UI...\n`);
|
|
214
291
|
});
|
|
292
|
+
|
|
293
|
+
// Initial scan and Cloud Relay registration
|
|
294
|
+
try {
|
|
295
|
+
const initialContext = await scanCodebase(PROJECT_ROOT);
|
|
296
|
+
await sendCloudRelayRequest('POST', '/api/v1/bridge/heartbeat', {
|
|
297
|
+
pair_code: PAIR_CODE,
|
|
298
|
+
project_name: path.basename(PROJECT_ROOT),
|
|
299
|
+
project_root: PROJECT_ROOT,
|
|
300
|
+
framework: initialContext.framework,
|
|
301
|
+
context: initialContext,
|
|
302
|
+
version: '1.1.5'
|
|
303
|
+
});
|
|
304
|
+
} catch (e) {}
|
|
305
|
+
|
|
306
|
+
// Periodic Cloud Relay heartbeat & write execution loop (every 3.5 seconds)
|
|
307
|
+
setInterval(async () => {
|
|
308
|
+
try {
|
|
309
|
+
const context = await scanCodebase(PROJECT_ROOT);
|
|
310
|
+
const hbResult = await sendCloudRelayRequest('POST', '/api/v1/bridge/heartbeat', {
|
|
311
|
+
pair_code: PAIR_CODE,
|
|
312
|
+
project_name: path.basename(PROJECT_ROOT),
|
|
313
|
+
project_root: PROJECT_ROOT,
|
|
314
|
+
framework: context.framework,
|
|
315
|
+
context: context,
|
|
316
|
+
version: '1.1.5'
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
// If pending write tasks, fetch and execute them on local disk
|
|
320
|
+
if (hbResult && hbResult.data && hbResult.data.pending_writes_count > 0) {
|
|
321
|
+
const pollResult = await sendCloudRelayRequest('GET', `/api/v1/bridge/poll-writes?pair_code=${PAIR_CODE}`);
|
|
322
|
+
const tasks = (pollResult.data && pollResult.data.pending_tasks) || [];
|
|
323
|
+
for (const task of tasks) {
|
|
324
|
+
const written = [];
|
|
325
|
+
for (const file of (task.files || [])) {
|
|
326
|
+
const fullPath = path.join(PROJECT_ROOT, file.path);
|
|
327
|
+
await fs.mkdir(path.dirname(fullPath), { recursive: true });
|
|
328
|
+
await fs.writeFile(fullPath, file.content, 'utf8');
|
|
329
|
+
console.log(`[Yap2App Bridge] ✍️ Wrote approved file: ${file.path}`);
|
|
330
|
+
written.push(file.path);
|
|
331
|
+
}
|
|
332
|
+
await sendCloudRelayRequest('POST', '/api/v1/bridge/complete-write', {
|
|
333
|
+
task_id: task.task_id,
|
|
334
|
+
success: true,
|
|
335
|
+
written,
|
|
336
|
+
message: `Wrote ${written.length} file(s) directly to ${path.basename(PROJECT_ROOT)}`
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
} catch {}
|
|
341
|
+
}, 3500);
|
|
215
342
|
}
|
|
216
343
|
|
|
217
344
|
// Direct execution
|
|
@@ -219,3 +346,5 @@ const isDirectRun = Boolean(typeof process !== 'undefined' && process.argv && pr
|
|
|
219
346
|
if (isDirectRun) {
|
|
220
347
|
startBridge();
|
|
221
348
|
}
|
|
349
|
+
|
|
350
|
+
|
package/package.json
CHANGED
package/polyfills.js
CHANGED
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Universal Node.js Polyfills for Yap2App CLI
|
|
3
|
-
* Enables full backwards compatibility across all NVM Node versions (Node 10.x to 23.x+)
|
|
2
|
+
* Universal Node.js Polyfills & Warning Suppressors for Yap2App CLI
|
|
3
|
+
* Enables full backwards compatibility and clean startup across all NVM Node versions (Node 10.x to 23.x+)
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
// 0. Suppress noisy Node 18 experimental warnings and punycode deprecation warnings (DEP0040)
|
|
7
|
+
if (typeof process !== 'undefined' && process.emitWarning) {
|
|
8
|
+
var _originalEmitWarning = process.emitWarning;
|
|
9
|
+
process.emitWarning = function (warning) {
|
|
10
|
+
if (typeof warning === 'string') {
|
|
11
|
+
if (warning.includes('punycode') || warning.includes('ExperimentalWarning') || warning.includes('DEP0040')) {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
} else if (warning && typeof warning === 'object') {
|
|
15
|
+
if (warning.code === 'DEP0040' || warning.name === 'ExperimentalWarning') {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
if (String(warning.message || '').includes('punycode')) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return _originalEmitWarning.apply(process, arguments);
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
6
26
|
// 1. globalThis polyfill (Node <12)
|
|
7
27
|
if (typeof globalThis === 'undefined') {
|
|
8
28
|
if (typeof global !== 'undefined') {
|
|
@@ -19,8 +39,9 @@ if (typeof Object.fromEntries === 'undefined') {
|
|
|
19
39
|
Object.fromEntries = function (entries) {
|
|
20
40
|
if (!entries) return {};
|
|
21
41
|
var obj = {};
|
|
22
|
-
|
|
23
|
-
|
|
42
|
+
var arr = Array.isArray(entries) ? entries : Array.from(entries);
|
|
43
|
+
for (var i = 0; i < arr.length; i++) {
|
|
44
|
+
var pair = arr[i];
|
|
24
45
|
if (pair && pair.length >= 2) {
|
|
25
46
|
obj[pair[0]] = pair[1];
|
|
26
47
|
}
|
|
@@ -64,7 +85,28 @@ if (!String.prototype.replaceAll) {
|
|
|
64
85
|
};
|
|
65
86
|
}
|
|
66
87
|
|
|
67
|
-
// 6.
|
|
88
|
+
// 6. String.prototype.matchAll polyfill (Node <12.0)
|
|
89
|
+
if (!String.prototype.matchAll) {
|
|
90
|
+
String.prototype.matchAll = function (regexp) {
|
|
91
|
+
var flags = regexp.flags;
|
|
92
|
+
if (!flags.includes('g')) {
|
|
93
|
+
flags += 'g';
|
|
94
|
+
}
|
|
95
|
+
var rx = new RegExp(regexp.source, flags);
|
|
96
|
+
var str = String(this);
|
|
97
|
+
var matches = [];
|
|
98
|
+
var match;
|
|
99
|
+
while ((match = rx.exec(str)) !== null) {
|
|
100
|
+
matches.push(match);
|
|
101
|
+
if (match.index === rx.lastIndex) {
|
|
102
|
+
rx.lastIndex++;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return matches[Symbol.iterator] ? matches[Symbol.iterator]() : matches;
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// 7. Promise.allSettled polyfill (Node <12.9)
|
|
68
110
|
if (!Promise.allSettled) {
|
|
69
111
|
Promise.allSettled = function (promises) {
|
|
70
112
|
return Promise.all(
|
|
@@ -81,3 +123,4 @@ if (!Promise.allSettled) {
|
|
|
81
123
|
);
|
|
82
124
|
};
|
|
83
125
|
}
|
|
126
|
+
|