@fxz26284407/yuanma-gateway 0.1.3 → 0.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/yuanma-gateway.mjs +0 -0
- package/dist/antigravity-native.js +32 -5
- package/dist/server.js +5 -2
- package/dist/version.js +2 -0
- package/package.json +2 -1
package/bin/yuanma-gateway.mjs
CHANGED
|
File without changes
|
|
@@ -11,11 +11,34 @@ const binary = '/Applications/Antigravity.app/Contents/Resources/bin/language_se
|
|
|
11
11
|
export class AntigravityNative {
|
|
12
12
|
#authority;
|
|
13
13
|
#checked = 0;
|
|
14
|
+
#connecting;
|
|
15
|
+
#epoch = 0;
|
|
14
16
|
ready = false;
|
|
15
17
|
get generation() { return this.#authority ? this.#authority.pid + ':' + this.#authority.port : ''; }
|
|
16
18
|
async connect() {
|
|
17
|
-
|
|
19
|
+
// ponytail: recheck ownership every 30s; failures invalidate sooner, without replaying requests.
|
|
20
|
+
if (this.ready && Date.now() - this.#checked < 30000)
|
|
18
21
|
return;
|
|
22
|
+
if (this.#connecting)
|
|
23
|
+
return this.#connecting;
|
|
24
|
+
const epoch = this.#epoch, pending = this.discover(epoch);
|
|
25
|
+
this.#connecting = pending;
|
|
26
|
+
try {
|
|
27
|
+
await pending;
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
if (epoch === this.#epoch) {
|
|
31
|
+
this.ready = false;
|
|
32
|
+
this.#checked = 0;
|
|
33
|
+
}
|
|
34
|
+
throw error;
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
if (this.#connecting === pending)
|
|
38
|
+
this.#connecting = undefined;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async discover(epoch) {
|
|
19
42
|
requireValue(process.platform === 'darwin', 'BACKEND_UNAVAILABLE', 'Antigravity 桌面接入需要 macOS', 503);
|
|
20
43
|
const version = JSON.parse(await readFile(app + '/package.json', 'utf8')).version;
|
|
21
44
|
requireValue(version === '2.29.0', 'PROTOCOL_INCOMPATIBLE', '当前仅适配 Antigravity App 2.29.0,请核对桌面版本', 503);
|
|
@@ -34,7 +57,7 @@ export class AntigravityNative {
|
|
|
34
57
|
const desktop = processes[0];
|
|
35
58
|
const { stdout: listeners } = await exec('/usr/sbin/lsof', ['-nP', '-a', '-p', String(desktop.pid), '-iTCP', '-sTCP:LISTEN', '-Fn'], { maxBuffer: 65536 });
|
|
36
59
|
const ports = [...new Set([...listeners.matchAll(/^n(?:127\.0\.0\.1|\*|\[::1\]):(\d+)$/gm)].map(m => Number(m[1])))];
|
|
37
|
-
requireValue(ports.length > 0
|
|
60
|
+
requireValue(ports.length > 0, 'BACKEND_UNAVAILABLE', '无法核实 Antigravity 桌面监听地址', 503);
|
|
38
61
|
const executable = await stat(binary);
|
|
39
62
|
requireValue(executable.isFile() && !(executable.mode & 0o002), 'UNSAFE_STORAGE', 'Antigravity 可执行文件权限异常', 403);
|
|
40
63
|
let found;
|
|
@@ -50,6 +73,7 @@ export class AntigravityNative {
|
|
|
50
73
|
catch { /* Only ports owned by the verified desktop process are considered. */ }
|
|
51
74
|
}
|
|
52
75
|
requireValue(found, 'BACKEND_UNAVAILABLE', 'Antigravity 桌面会话接口未就绪', 503);
|
|
76
|
+
requireValue(epoch === this.#epoch, 'BACKEND_UNAVAILABLE', 'Antigravity 连接已关闭', 503);
|
|
53
77
|
this.#authority = found;
|
|
54
78
|
this.#checked = Date.now();
|
|
55
79
|
this.ready = true;
|
|
@@ -61,7 +85,7 @@ export class AntigravityNative {
|
|
|
61
85
|
return await this.rpc(authority, method, params, timeout, signal);
|
|
62
86
|
}
|
|
63
87
|
catch (error) {
|
|
64
|
-
if (!signal?.aborted) {
|
|
88
|
+
if (!signal?.aborted && this.#authority === authority) {
|
|
65
89
|
this.ready = false;
|
|
66
90
|
this.#checked = 0;
|
|
67
91
|
}
|
|
@@ -91,7 +115,10 @@ export class AntigravityNative {
|
|
|
91
115
|
payload.copy(frame, 5);
|
|
92
116
|
let ended = false, received = false;
|
|
93
117
|
const finish = (error) => { if (ended)
|
|
94
|
-
return; ended = true; clearTimeout(first); req.destroy();
|
|
118
|
+
return; ended = true; clearTimeout(first); req.destroy(); if (error && this.#authority === authority) {
|
|
119
|
+
this.ready = false;
|
|
120
|
+
this.#checked = 0;
|
|
121
|
+
} onEnd(error); };
|
|
95
122
|
const req = request({ hostname: '127.0.0.1', port: authority.port, path: '/exa.language_server_pb.LanguageServerService/' + method, method: 'POST', rejectUnauthorized: false, headers: { 'content-type': 'application/connect+json', 'connect-protocol-version': '1', 'x-codeium-csrf-token': authority.token } }, res => {
|
|
96
123
|
if (res.statusCode !== 200) {
|
|
97
124
|
finish(new Error('Antigravity 状态订阅不可用(HTTP ' + res.statusCode + ')'));
|
|
@@ -180,5 +207,5 @@ export class AntigravityNative {
|
|
|
180
207
|
req.end(payload);
|
|
181
208
|
});
|
|
182
209
|
}
|
|
183
|
-
close() { this.#authority = undefined; this.#checked = 0; this.ready = false; }
|
|
210
|
+
close() { this.#epoch++; this.#connecting = undefined; this.#authority = undefined; this.#checked = 0; this.ready = false; }
|
|
184
211
|
}
|
package/dist/server.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { GATEWAY_VERSION } from './version.js';
|
|
1
2
|
import Database from 'better-sqlite3';
|
|
2
3
|
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
3
4
|
import { ProjectAccess } from './project-access.js';
|
|
@@ -173,7 +174,7 @@ export async function startGateway(options) {
|
|
|
173
174
|
}
|
|
174
175
|
await projectAccess.sync(true);
|
|
175
176
|
const supports = (key) => requireValue(current().backend.capabilities[key], 'UNSUPPORTED_COMMAND', 'Backend does not support ' + key, 400);
|
|
176
|
-
const backendStatus = () => ({ protocolVersion: 1, backendReady: current().backend.ready, backendKind: current().backend.kind, readOnly: current().backend.readOnly === true, uploadLimits, capabilities: { ...current().backend.capabilities, createThreads: !!current().backend.createThread }, availableBackends: [...contexts.keys()] });
|
|
177
|
+
const backendStatus = () => ({ gatewayVersion: GATEWAY_VERSION, protocolVersion: 1, backendReady: current().backend.ready, backendKind: current().backend.kind, readOnly: current().backend.readOnly === true, uploadLimits, capabilities: { ...current().backend.capabilities, createThreads: !!current().backend.createThread }, availableBackends: [...contexts.keys()] });
|
|
177
178
|
let stopping = false;
|
|
178
179
|
const pending = new Set();
|
|
179
180
|
const track = (p) => { pending.add(p); void p.finally(() => pending.delete(p)).catch(() => { }); return p; };
|
|
@@ -511,6 +512,8 @@ export async function startGateway(options) {
|
|
|
511
512
|
async function secureRequest(req, binding) {
|
|
512
513
|
const data = req.body ?? {};
|
|
513
514
|
const path = req.path;
|
|
515
|
+
if (req.method === 'GET' && path === '/v1/gateway-info')
|
|
516
|
+
return { gatewayVersion: GATEWAY_VERSION };
|
|
514
517
|
const value = (prefix) => req.authorization?.startsWith(prefix) ? req.authorization.slice(prefix.length) : '';
|
|
515
518
|
if (req.method === 'POST' && path === '/v1/pairings/claim')
|
|
516
519
|
return auth.claim(data, binding);
|
|
@@ -687,7 +690,7 @@ export async function startGateway(options) {
|
|
|
687
690
|
if (req.method === 'POST' && url.pathname === '/local/pairings/open') {
|
|
688
691
|
await projectAccess.sync(true);
|
|
689
692
|
const b = await body(req);
|
|
690
|
-
send(res, 200, { requestId, data: { ...auth.openPairing(b.grants), publicKey: identity.publicKey.toString('base64url'), profile: NOISE_PROFILE, relayUrl: options.relayUrl ?? null } });
|
|
693
|
+
send(res, 200, { requestId, data: { ...auth.openPairing(b.grants), publicKey: identity.publicKey.toString('base64url'), profile: NOISE_PROFILE, gatewayVersion: GATEWAY_VERSION, relayUrl: options.relayUrl ?? null } });
|
|
691
694
|
return;
|
|
692
695
|
}
|
|
693
696
|
if (req.method === 'GET' && url.pathname === '/local/pairings') {
|
package/dist/version.js
ADDED