@techsologic/unolock-agent 0.1.37 → 0.1.39
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/unolock-agent.js +136 -16
- package/package.json +1 -1
package/bin/unolock-agent.js
CHANGED
|
@@ -7,9 +7,12 @@ const path = require("path");
|
|
|
7
7
|
const https = require("https");
|
|
8
8
|
const { spawn } = require("child_process");
|
|
9
9
|
|
|
10
|
-
const PACKAGE_VERSION = "0.1.
|
|
11
|
-
const FALLBACK_BINARY_VERSION = "0.1.
|
|
10
|
+
const PACKAGE_VERSION = "0.1.39";
|
|
11
|
+
const FALLBACK_BINARY_VERSION = "0.1.39";
|
|
12
12
|
const REPO = "TechSologic/unolock-agent";
|
|
13
|
+
const INSTALL_LOCK_TIMEOUT_MS = 120000;
|
|
14
|
+
const INSTALL_LOCK_STALE_MS = 300000;
|
|
15
|
+
const INSTALL_LOCK_POLL_MS = 100;
|
|
13
16
|
const TOP_LEVEL_USAGE = `usage: unolock-agent [-h] [--version] {register,set-agent-pin,list-spaces,get-current-space,set-current-space,list-records,list-notes,list-checklists,get-record,create-note,update-note,append-note,rename-record,create-checklist,set-checklist-item-done,add-checklist-item,remove-checklist-item,list-files,get-file,download-file,upload-file,rename-file,replace-file,delete-file,tpm-diagnose,tpm-check,self-test,mcp} ...
|
|
14
17
|
|
|
15
18
|
UnoLock Agent commands.
|
|
@@ -79,6 +82,10 @@ function metadataPath() {
|
|
|
79
82
|
return path.join(cacheRoot(), "unolock-agent", "release.json");
|
|
80
83
|
}
|
|
81
84
|
|
|
85
|
+
function installLockPath() {
|
|
86
|
+
return path.join(cacheRoot(), "unolock-agent", "install.lock");
|
|
87
|
+
}
|
|
88
|
+
|
|
82
89
|
function binaryPath(releaseVersion) {
|
|
83
90
|
const { executable } = platformAssetInfo();
|
|
84
91
|
return path.join(cacheRoot(), "unolock-agent", releaseVersion, executable);
|
|
@@ -96,6 +103,12 @@ function ensureDir(dir) {
|
|
|
96
103
|
fs.mkdirSync(dir, { recursive: true, mode: 0o755 });
|
|
97
104
|
}
|
|
98
105
|
|
|
106
|
+
function sleep(ms) {
|
|
107
|
+
return new Promise((resolve) => {
|
|
108
|
+
setTimeout(resolve, ms);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
99
112
|
function fetchToFile(url, dest) {
|
|
100
113
|
return new Promise((resolve, reject) => {
|
|
101
114
|
const temp = `${dest}.download`;
|
|
@@ -188,6 +201,23 @@ function normalizeVersion(value) {
|
|
|
188
201
|
return trimmed.startsWith("v") ? trimmed.slice(1) : trimmed;
|
|
189
202
|
}
|
|
190
203
|
|
|
204
|
+
function compareVersions(left, right) {
|
|
205
|
+
const leftParts = String(left || "").split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
206
|
+
const rightParts = String(right || "").split(".").map((part) => Number.parseInt(part, 10) || 0);
|
|
207
|
+
const length = Math.max(leftParts.length, rightParts.length);
|
|
208
|
+
for (let index = 0; index < length; index += 1) {
|
|
209
|
+
const leftValue = leftParts[index] || 0;
|
|
210
|
+
const rightValue = rightParts[index] || 0;
|
|
211
|
+
if (leftValue > rightValue) {
|
|
212
|
+
return 1;
|
|
213
|
+
}
|
|
214
|
+
if (leftValue < rightValue) {
|
|
215
|
+
return -1;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return 0;
|
|
219
|
+
}
|
|
220
|
+
|
|
191
221
|
function readReleaseMetadata() {
|
|
192
222
|
try {
|
|
193
223
|
return JSON.parse(fs.readFileSync(metadataPath(), "utf8"));
|
|
@@ -198,8 +228,10 @@ function readReleaseMetadata() {
|
|
|
198
228
|
|
|
199
229
|
function writeReleaseMetadata(releaseVersion) {
|
|
200
230
|
ensureDir(path.dirname(metadataPath()));
|
|
231
|
+
const dest = metadataPath();
|
|
232
|
+
const temp = `${dest}.tmp-${process.pid}`;
|
|
201
233
|
fs.writeFileSync(
|
|
202
|
-
|
|
234
|
+
temp,
|
|
203
235
|
JSON.stringify(
|
|
204
236
|
{
|
|
205
237
|
releaseVersion,
|
|
@@ -210,6 +242,7 @@ function writeReleaseMetadata(releaseVersion) {
|
|
|
210
242
|
),
|
|
211
243
|
"utf8"
|
|
212
244
|
);
|
|
245
|
+
fs.renameSync(temp, dest);
|
|
213
246
|
}
|
|
214
247
|
|
|
215
248
|
function cachedReleaseVersion() {
|
|
@@ -218,8 +251,11 @@ function cachedReleaseVersion() {
|
|
|
218
251
|
return override;
|
|
219
252
|
}
|
|
220
253
|
const metadata = readReleaseMetadata();
|
|
221
|
-
|
|
222
|
-
|
|
254
|
+
const cached = metadata && typeof metadata.releaseVersion === "string" ? normalizeVersion(metadata.releaseVersion) : null;
|
|
255
|
+
if (cached && fs.existsSync(binaryPath(cached))) {
|
|
256
|
+
if (compareVersions(cached, FALLBACK_BINARY_VERSION) >= 0) {
|
|
257
|
+
return cached;
|
|
258
|
+
}
|
|
223
259
|
}
|
|
224
260
|
if (fs.existsSync(binaryPath(FALLBACK_BINARY_VERSION))) {
|
|
225
261
|
return FALLBACK_BINARY_VERSION;
|
|
@@ -246,16 +282,88 @@ async function resolveReleaseVersion() {
|
|
|
246
282
|
return FALLBACK_BINARY_VERSION;
|
|
247
283
|
}
|
|
248
284
|
|
|
285
|
+
function removeIfStale(lockPath) {
|
|
286
|
+
let stats;
|
|
287
|
+
try {
|
|
288
|
+
stats = fs.statSync(lockPath);
|
|
289
|
+
} catch (error) {
|
|
290
|
+
if (error && error.code === "ENOENT") {
|
|
291
|
+
return false;
|
|
292
|
+
}
|
|
293
|
+
throw error;
|
|
294
|
+
}
|
|
295
|
+
if (Date.now() - stats.mtimeMs < INSTALL_LOCK_STALE_MS) {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
try {
|
|
299
|
+
fs.unlinkSync(lockPath);
|
|
300
|
+
return true;
|
|
301
|
+
} catch (error) {
|
|
302
|
+
if (error && error.code === "ENOENT") {
|
|
303
|
+
return true;
|
|
304
|
+
}
|
|
305
|
+
if (error && error.code === "EPERM") {
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
throw error;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async function acquireInstallLock() {
|
|
313
|
+
const lockPath = installLockPath();
|
|
314
|
+
ensureDir(path.dirname(lockPath));
|
|
315
|
+
const deadline = Date.now() + INSTALL_LOCK_TIMEOUT_MS;
|
|
316
|
+
while (true) {
|
|
317
|
+
try {
|
|
318
|
+
const fd = fs.openSync(lockPath, "wx", 0o600);
|
|
319
|
+
fs.writeFileSync(
|
|
320
|
+
fd,
|
|
321
|
+
JSON.stringify({ pid: process.pid, createdAt: Date.now() }),
|
|
322
|
+
"utf8"
|
|
323
|
+
);
|
|
324
|
+
return () => {
|
|
325
|
+
try {
|
|
326
|
+
fs.closeSync(fd);
|
|
327
|
+
} catch {}
|
|
328
|
+
try {
|
|
329
|
+
fs.unlinkSync(lockPath);
|
|
330
|
+
} catch {}
|
|
331
|
+
};
|
|
332
|
+
} catch (error) {
|
|
333
|
+
if (!error || error.code !== "EEXIST") {
|
|
334
|
+
throw error;
|
|
335
|
+
}
|
|
336
|
+
removeIfStale(lockPath);
|
|
337
|
+
if (Date.now() >= deadline) {
|
|
338
|
+
throw new Error("Timed out waiting for the UnoLock agent install lock");
|
|
339
|
+
}
|
|
340
|
+
await sleep(INSTALL_LOCK_POLL_MS);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
|
|
249
345
|
async function ensureBinary() {
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
346
|
+
const cached = cachedReleaseVersion();
|
|
347
|
+
if (cached) {
|
|
348
|
+
const dest = binaryPath(cached);
|
|
349
|
+
if (fs.existsSync(dest)) {
|
|
350
|
+
return { dest, releaseVersion: cached };
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
const releaseLock = await acquireInstallLock();
|
|
354
|
+
try {
|
|
355
|
+
const releaseVersion = await resolveReleaseVersion();
|
|
356
|
+
const dest = binaryPath(releaseVersion);
|
|
357
|
+
if (fs.existsSync(dest)) {
|
|
358
|
+
return { dest, releaseVersion };
|
|
359
|
+
}
|
|
360
|
+
ensureDir(path.dirname(dest));
|
|
361
|
+
process.stderr.write(`Downloading UnoLock agent ${releaseVersion} for ${process.platform}/${process.arch}...\n`);
|
|
362
|
+
await fetchToFile(binaryUrl(releaseVersion), dest);
|
|
253
363
|
return { dest, releaseVersion };
|
|
364
|
+
} finally {
|
|
365
|
+
releaseLock();
|
|
254
366
|
}
|
|
255
|
-
ensureDir(path.dirname(dest));
|
|
256
|
-
process.stderr.write(`Downloading UnoLock agent ${releaseVersion} for ${process.platform}/${process.arch}...\n`);
|
|
257
|
-
await fetchToFile(binaryUrl(releaseVersion), dest);
|
|
258
|
-
return { dest, releaseVersion };
|
|
259
367
|
}
|
|
260
368
|
|
|
261
369
|
async function main() {
|
|
@@ -291,7 +399,19 @@ async function main() {
|
|
|
291
399
|
});
|
|
292
400
|
}
|
|
293
401
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
402
|
+
if (require.main === module) {
|
|
403
|
+
main().catch((error) => {
|
|
404
|
+
process.stderr.write(`${error.message}\n`);
|
|
405
|
+
process.exit(1);
|
|
406
|
+
});
|
|
407
|
+
} else {
|
|
408
|
+
module.exports = {
|
|
409
|
+
acquireInstallLock,
|
|
410
|
+
compareVersions,
|
|
411
|
+
ensureBinary,
|
|
412
|
+
installLockPath,
|
|
413
|
+
metadataPath,
|
|
414
|
+
sleep,
|
|
415
|
+
writeReleaseMetadata,
|
|
416
|
+
};
|
|
417
|
+
}
|