blun-king-cli 9.1.10 → 9.1.12
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/blun.js +121 -25
- package/bin/core-bootstrap.js +3 -3
- package/bin/king.js +121 -25
- package/bin/launcher-runtime.js +37 -19
- package/bin/update-lease.js +206 -8
- package/blun.mjs +2878 -438
- package/package.json +48 -50
- package/standard-tools/language-guard/blun_language_guard.py +22 -3
- package/telegram-plugin/dist/bridge.mjs +1 -129
- package/telegram-plugin/dist/mcp-server.mjs +2 -10
package/bin/update-lease.js
CHANGED
|
@@ -5,6 +5,7 @@ const fs = require('node:fs');
|
|
|
5
5
|
const net = require('node:net');
|
|
6
6
|
const os = require('node:os');
|
|
7
7
|
const path = require('node:path');
|
|
8
|
+
const { spawn } = require('node:child_process');
|
|
8
9
|
|
|
9
10
|
const LEASE_ADOPT_MESSAGE = 'blun-update-lease-adopted';
|
|
10
11
|
const LEASE_CANCEL_MESSAGE = 'blun-update-lease-cancel';
|
|
@@ -15,6 +16,7 @@ const LEASE_UNAVAILABLE_MESSAGE = 'blun-update-lease-unavailable';
|
|
|
15
16
|
const LEASE_PROTOCOL_VERSION = 'BLUN_UPDATE_LEASE_V3';
|
|
16
17
|
const LEASE_PROBE_TIMEOUT_MS = 500;
|
|
17
18
|
const MAX_POSIX_SOCKET_PATH_BYTES = 96;
|
|
19
|
+
const LEASE_CONNECTIONS = Symbol('blunLeaseConnections');
|
|
18
20
|
|
|
19
21
|
function canonicalPackageIdentity(packageRoot, options = {}) {
|
|
20
22
|
if (typeof packageRoot !== 'string' || packageRoot.trim().length === 0) {
|
|
@@ -32,7 +34,11 @@ function canonicalPackageIdentity(packageRoot, options = {}) {
|
|
|
32
34
|
|
|
33
35
|
function deriveUpdateLeaseEndpoint(packageRoot, options = {}) {
|
|
34
36
|
const platform = options.platform || process.platform;
|
|
35
|
-
const role = options.role === 'install'
|
|
37
|
+
const role = options.role === 'install'
|
|
38
|
+
? 'install'
|
|
39
|
+
: options.role === 'runtime'
|
|
40
|
+
? 'runtime'
|
|
41
|
+
: 'notice';
|
|
36
42
|
const identity = canonicalPackageIdentity(packageRoot, options);
|
|
37
43
|
const digest = createHash('sha256')
|
|
38
44
|
.update(`blun-update-v3\0${role}\0${identity}`)
|
|
@@ -64,20 +70,201 @@ function deriveUpdateLeaseEndpoint(packageRoot, options = {}) {
|
|
|
64
70
|
return Object.freeze({ directory, magic, path: socketPath, platform });
|
|
65
71
|
}
|
|
66
72
|
|
|
67
|
-
function
|
|
73
|
+
function connectRuntimeBroker(endpoint, options = {}) {
|
|
74
|
+
const netImpl = options.netImpl || net;
|
|
75
|
+
const timeoutMs = options.probeTimeoutMs || LEASE_PROBE_TIMEOUT_MS;
|
|
68
76
|
return new Promise((resolve) => {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
77
|
+
let settled = false;
|
|
78
|
+
let received = '';
|
|
79
|
+
let socket;
|
|
80
|
+
const finish = (result) => {
|
|
81
|
+
if (settled) return;
|
|
82
|
+
settled = true;
|
|
83
|
+
clearTimeout(timer);
|
|
84
|
+
if (!result.acquired) {
|
|
85
|
+
try {
|
|
86
|
+
socket?.destroy();
|
|
87
|
+
} catch {}
|
|
88
|
+
}
|
|
89
|
+
resolve(result);
|
|
90
|
+
};
|
|
91
|
+
const timer = setTimeout(() => finish({ acquired: false, reason: 'unavailable' }), timeoutMs);
|
|
92
|
+
timer.unref?.();
|
|
73
93
|
try {
|
|
74
|
-
|
|
94
|
+
socket = netImpl.createConnection({ path: endpoint.path });
|
|
95
|
+
socket.setEncoding?.('utf8');
|
|
96
|
+
socket.once('connect', () => socket.write(endpoint.magic));
|
|
97
|
+
socket.on('data', (chunk) => {
|
|
98
|
+
received += String(chunk);
|
|
99
|
+
if (!received.includes('\n')) return;
|
|
100
|
+
if (received !== endpoint.magic) {
|
|
101
|
+
finish({ acquired: false, reason: 'unavailable' });
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
let owned = true;
|
|
105
|
+
let released = false;
|
|
106
|
+
socket.once('close', () => { owned = false; });
|
|
107
|
+
socket.once('error', () => { owned = false; });
|
|
108
|
+
const lease = Object.freeze({
|
|
109
|
+
assertOwned() {
|
|
110
|
+
if (!owned || socket.destroyed) throw new Error('RUNTIME_LEASE_LOST');
|
|
111
|
+
},
|
|
112
|
+
async release() {
|
|
113
|
+
if (released) return;
|
|
114
|
+
released = true;
|
|
115
|
+
owned = false;
|
|
116
|
+
if (socket.destroyed) return;
|
|
117
|
+
socket.destroy();
|
|
118
|
+
},
|
|
119
|
+
unref() {
|
|
120
|
+
socket.unref?.();
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
finish({ acquired: true, lease });
|
|
124
|
+
});
|
|
125
|
+
socket.once('error', () => finish({ acquired: false, reason: 'unavailable' }));
|
|
126
|
+
socket.once('close', () => finish({ acquired: false, reason: 'unavailable' }));
|
|
75
127
|
} catch {
|
|
76
|
-
|
|
128
|
+
finish({ acquired: false, reason: 'unavailable' });
|
|
77
129
|
}
|
|
78
130
|
});
|
|
79
131
|
}
|
|
80
132
|
|
|
133
|
+
function spawnRuntimeBroker(packageRoot, options = {}) {
|
|
134
|
+
const spawnImpl = options.spawnImpl || spawn;
|
|
135
|
+
const child = spawnImpl(
|
|
136
|
+
options.execPath || process.execPath,
|
|
137
|
+
[__filename, '--runtime-broker'],
|
|
138
|
+
{
|
|
139
|
+
cwd: os.tmpdir(),
|
|
140
|
+
detached: true,
|
|
141
|
+
env: {
|
|
142
|
+
...process.env,
|
|
143
|
+
BLUN_RUNTIME_BROKER_PACKAGE_ROOT: packageRoot,
|
|
144
|
+
},
|
|
145
|
+
stdio: 'ignore',
|
|
146
|
+
windowsHide: true,
|
|
147
|
+
},
|
|
148
|
+
);
|
|
149
|
+
child.unref?.();
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async function acquireSharedRuntimeLease(options = {}) {
|
|
153
|
+
const packageRoot = canonicalPackageIdentity(options.packageRoot, options);
|
|
154
|
+
const endpoint = deriveUpdateLeaseEndpoint(packageRoot, { ...options, role: 'runtime' });
|
|
155
|
+
const deadline = Date.now() + (options.startTimeoutMs || 3_000);
|
|
156
|
+
let spawned = false;
|
|
157
|
+
while (Date.now() < deadline) {
|
|
158
|
+
const connected = await connectRuntimeBroker(endpoint, options);
|
|
159
|
+
if (connected.acquired) return connected;
|
|
160
|
+
if (!spawned) {
|
|
161
|
+
spawned = true;
|
|
162
|
+
spawnRuntimeBroker(packageRoot, options);
|
|
163
|
+
}
|
|
164
|
+
await new Promise((resolve) => setTimeout(resolve, 25));
|
|
165
|
+
}
|
|
166
|
+
const install = await tryAcquireUpdateLease({ ...options, packageRoot, role: 'install' });
|
|
167
|
+
if (install.acquired) {
|
|
168
|
+
await install.lease.release();
|
|
169
|
+
return { acquired: false, reason: 'unavailable' };
|
|
170
|
+
}
|
|
171
|
+
return { acquired: false, reason: install.reason };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
async function runRuntimeBroker(options = {}) {
|
|
175
|
+
const packageRoot = options.packageRoot
|
|
176
|
+
|| String(process.env.BLUN_RUNTIME_BROKER_PACKAGE_ROOT || '');
|
|
177
|
+
if (!path.isAbsolute(packageRoot)) throw new Error('RUNTIME_BROKER_PACKAGE_ROOT_REQUIRED');
|
|
178
|
+
const install = await tryAcquireUpdateLease({ packageRoot, role: 'install' });
|
|
179
|
+
if (!install.acquired) return false;
|
|
180
|
+
|
|
181
|
+
const endpoint = deriveUpdateLeaseEndpoint(packageRoot, { role: 'runtime' });
|
|
182
|
+
ensurePrivateSocketDirectory(endpoint);
|
|
183
|
+
const holders = new Set();
|
|
184
|
+
let idleTimer;
|
|
185
|
+
let closing = false;
|
|
186
|
+
let identity;
|
|
187
|
+
const closeBroker = async (server) => {
|
|
188
|
+
if (closing) return;
|
|
189
|
+
closing = true;
|
|
190
|
+
clearTimeout(idleTimer);
|
|
191
|
+
await closeServer(server);
|
|
192
|
+
removeOwnedSocket(endpoint, identity);
|
|
193
|
+
await install.lease.release();
|
|
194
|
+
};
|
|
195
|
+
const server = net.createServer((socket) => {
|
|
196
|
+
socket.setEncoding('utf8');
|
|
197
|
+
let received = '';
|
|
198
|
+
let admitted = false;
|
|
199
|
+
const reject = () => {
|
|
200
|
+
try {
|
|
201
|
+
socket.destroy();
|
|
202
|
+
} catch {}
|
|
203
|
+
};
|
|
204
|
+
const onData = (chunk) => {
|
|
205
|
+
if (admitted) return;
|
|
206
|
+
received += String(chunk);
|
|
207
|
+
if (Buffer.byteLength(received, 'utf8') > Buffer.byteLength(endpoint.magic, 'utf8')) {
|
|
208
|
+
reject();
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
if (!received.includes('\n')) return;
|
|
212
|
+
if (received !== endpoint.magic) {
|
|
213
|
+
reject();
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
admitted = true;
|
|
217
|
+
clearTimeout(idleTimer);
|
|
218
|
+
holders.add(socket);
|
|
219
|
+
socket.write(endpoint.magic);
|
|
220
|
+
};
|
|
221
|
+
socket.on('data', onData);
|
|
222
|
+
socket.on('error', () => {});
|
|
223
|
+
socket.once('close', () => {
|
|
224
|
+
holders.delete(socket);
|
|
225
|
+
if (holders.size === 0 && !closing) {
|
|
226
|
+
idleTimer = setTimeout(() => { void closeBroker(server); }, 0);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
try {
|
|
231
|
+
await new Promise((resolve, reject) => {
|
|
232
|
+
const onError = (error) => {
|
|
233
|
+
server.removeListener('error', onError);
|
|
234
|
+
reject(error);
|
|
235
|
+
};
|
|
236
|
+
server.once('error', onError);
|
|
237
|
+
server.listen({ path: endpoint.path, exclusive: true }, () => {
|
|
238
|
+
server.removeListener('error', onError);
|
|
239
|
+
resolve();
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
identity = recordSocketIdentity(endpoint);
|
|
243
|
+
} catch {
|
|
244
|
+
await closeServer(server);
|
|
245
|
+
await install.lease.release();
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
install.lease.unref();
|
|
249
|
+
server.unref?.();
|
|
250
|
+
idleTimer = setTimeout(() => { void closeBroker(server); }, 5_000);
|
|
251
|
+
return true;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function closeServer(server) {
|
|
255
|
+
for (const socket of server[LEASE_CONNECTIONS] || []) {
|
|
256
|
+
try {
|
|
257
|
+
socket.destroy();
|
|
258
|
+
} catch {}
|
|
259
|
+
}
|
|
260
|
+
if (!server.listening) return Promise.resolve();
|
|
261
|
+
try {
|
|
262
|
+
server.close();
|
|
263
|
+
server.unref?.();
|
|
264
|
+
} catch {}
|
|
265
|
+
return Promise.resolve();
|
|
266
|
+
}
|
|
267
|
+
|
|
81
268
|
function ensurePrivateSocketDirectory(endpoint, options = {}) {
|
|
82
269
|
if (endpoint.platform === 'win32') return;
|
|
83
270
|
const fsImpl = options.fsImpl || fs;
|
|
@@ -98,9 +285,12 @@ function listenForLease(endpoint, options = {}) {
|
|
|
98
285
|
const netImpl = options.netImpl || net;
|
|
99
286
|
return new Promise((resolve, reject) => {
|
|
100
287
|
const server = netImpl.createServer((socket) => {
|
|
288
|
+
server[LEASE_CONNECTIONS].add(socket);
|
|
289
|
+
socket.once('close', () => server[LEASE_CONNECTIONS].delete(socket));
|
|
101
290
|
socket.on('error', () => {});
|
|
102
291
|
socket.end(endpoint.magic);
|
|
103
292
|
});
|
|
293
|
+
server[LEASE_CONNECTIONS] = new Set();
|
|
104
294
|
const onError = (error) => {
|
|
105
295
|
server.removeListener('error', onError);
|
|
106
296
|
reject(error);
|
|
@@ -391,9 +581,17 @@ function runInstallerWorker() {
|
|
|
391
581
|
|
|
392
582
|
if (require.main === module && process.argv[2] === '--installer-worker') {
|
|
393
583
|
runInstallerWorker();
|
|
584
|
+
} else if (require.main === module && process.argv[2] === '--runtime-broker') {
|
|
585
|
+
void runRuntimeBroker().then((started) => {
|
|
586
|
+
if (!started) process.exitCode = 75;
|
|
587
|
+
}).catch(() => {
|
|
588
|
+
process.exitCode = 1;
|
|
589
|
+
});
|
|
394
590
|
}
|
|
395
591
|
|
|
396
592
|
module.exports = {
|
|
593
|
+
acquireSharedRuntimeLease,
|
|
397
594
|
deriveUpdateLeaseEndpoint,
|
|
595
|
+
runRuntimeBroker,
|
|
398
596
|
tryAcquireUpdateLease,
|
|
399
597
|
};
|