@ariobarin/glossa 0.2.0-beta.2 → 0.2.0
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/README.md +4 -1
- package/dist/app.js +726 -474
- package/package.json +1 -1
package/dist/app.js
CHANGED
|
@@ -10436,10 +10436,10 @@ var require_react_reconciler_development = __commonJS({
|
|
|
10436
10436
|
fiber = fiber.next, id--;
|
|
10437
10437
|
return fiber;
|
|
10438
10438
|
}
|
|
10439
|
-
function copyWithSetImpl(obj,
|
|
10440
|
-
if (index >=
|
|
10441
|
-
var key =
|
|
10442
|
-
updated[key] = copyWithSetImpl(obj[key],
|
|
10439
|
+
function copyWithSetImpl(obj, path14, index, value) {
|
|
10440
|
+
if (index >= path14.length) return value;
|
|
10441
|
+
var key = path14[index], updated = isArrayImpl(obj) ? obj.slice() : assign({}, obj);
|
|
10442
|
+
updated[key] = copyWithSetImpl(obj[key], path14, index + 1, value);
|
|
10443
10443
|
return updated;
|
|
10444
10444
|
}
|
|
10445
10445
|
function copyWithRename(obj, oldPath, newPath) {
|
|
@@ -10466,11 +10466,11 @@ var require_react_reconciler_development = __commonJS({
|
|
|
10466
10466
|
);
|
|
10467
10467
|
return updated;
|
|
10468
10468
|
}
|
|
10469
|
-
function copyWithDeleteImpl(obj,
|
|
10470
|
-
var key =
|
|
10471
|
-
if (index + 1 ===
|
|
10469
|
+
function copyWithDeleteImpl(obj, path14, index) {
|
|
10470
|
+
var key = path14[index], updated = isArrayImpl(obj) ? obj.slice() : assign({}, obj);
|
|
10471
|
+
if (index + 1 === path14.length)
|
|
10472
10472
|
return isArrayImpl(updated) ? updated.splice(key, 1) : delete updated[key], updated;
|
|
10473
|
-
updated[key] = copyWithDeleteImpl(obj[key],
|
|
10473
|
+
updated[key] = copyWithDeleteImpl(obj[key], path14, index + 1);
|
|
10474
10474
|
return updated;
|
|
10475
10475
|
}
|
|
10476
10476
|
function shouldSuspendImpl() {
|
|
@@ -23747,29 +23747,29 @@ var require_react_reconciler_development = __commonJS({
|
|
|
23747
23747
|
var didWarnAboutNestedUpdates = false;
|
|
23748
23748
|
var didWarnAboutFindNodeInStrictMode = {};
|
|
23749
23749
|
var overrideHookState = null, overrideHookStateDeletePath = null, overrideHookStateRenamePath = null, overrideProps = null, overridePropsDeletePath = null, overridePropsRenamePath = null, scheduleUpdate = null, scheduleRetry = null, setErrorHandler = null, setSuspenseHandler = null;
|
|
23750
|
-
overrideHookState = function(fiber, id,
|
|
23750
|
+
overrideHookState = function(fiber, id, path14, value) {
|
|
23751
23751
|
id = findHook(fiber, id);
|
|
23752
|
-
null !== id && (
|
|
23752
|
+
null !== id && (path14 = copyWithSetImpl(id.memoizedState, path14, 0, value), id.memoizedState = path14, id.baseState = path14, fiber.memoizedProps = assign({}, fiber.memoizedProps), path14 = enqueueConcurrentRenderForLane(fiber, 2), null !== path14 && scheduleUpdateOnFiber(path14, fiber, 2));
|
|
23753
23753
|
};
|
|
23754
|
-
overrideHookStateDeletePath = function(fiber, id,
|
|
23754
|
+
overrideHookStateDeletePath = function(fiber, id, path14) {
|
|
23755
23755
|
id = findHook(fiber, id);
|
|
23756
|
-
null !== id && (
|
|
23756
|
+
null !== id && (path14 = copyWithDeleteImpl(id.memoizedState, path14, 0), id.memoizedState = path14, id.baseState = path14, fiber.memoizedProps = assign({}, fiber.memoizedProps), path14 = enqueueConcurrentRenderForLane(fiber, 2), null !== path14 && scheduleUpdateOnFiber(path14, fiber, 2));
|
|
23757
23757
|
};
|
|
23758
23758
|
overrideHookStateRenamePath = function(fiber, id, oldPath, newPath) {
|
|
23759
23759
|
id = findHook(fiber, id);
|
|
23760
23760
|
null !== id && (oldPath = copyWithRename(id.memoizedState, oldPath, newPath), id.memoizedState = oldPath, id.baseState = oldPath, fiber.memoizedProps = assign({}, fiber.memoizedProps), oldPath = enqueueConcurrentRenderForLane(fiber, 2), null !== oldPath && scheduleUpdateOnFiber(oldPath, fiber, 2));
|
|
23761
23761
|
};
|
|
23762
|
-
overrideProps = function(fiber,
|
|
23763
|
-
fiber.pendingProps = copyWithSetImpl(fiber.memoizedProps,
|
|
23762
|
+
overrideProps = function(fiber, path14, value) {
|
|
23763
|
+
fiber.pendingProps = copyWithSetImpl(fiber.memoizedProps, path14, 0, value);
|
|
23764
23764
|
fiber.alternate && (fiber.alternate.pendingProps = fiber.pendingProps);
|
|
23765
|
-
|
|
23766
|
-
null !==
|
|
23765
|
+
path14 = enqueueConcurrentRenderForLane(fiber, 2);
|
|
23766
|
+
null !== path14 && scheduleUpdateOnFiber(path14, fiber, 2);
|
|
23767
23767
|
};
|
|
23768
|
-
overridePropsDeletePath = function(fiber,
|
|
23769
|
-
fiber.pendingProps = copyWithDeleteImpl(fiber.memoizedProps,
|
|
23768
|
+
overridePropsDeletePath = function(fiber, path14) {
|
|
23769
|
+
fiber.pendingProps = copyWithDeleteImpl(fiber.memoizedProps, path14, 0);
|
|
23770
23770
|
fiber.alternate && (fiber.alternate.pendingProps = fiber.pendingProps);
|
|
23771
|
-
|
|
23772
|
-
null !==
|
|
23771
|
+
path14 = enqueueConcurrentRenderForLane(fiber, 2);
|
|
23772
|
+
null !== path14 && scheduleUpdateOnFiber(path14, fiber, 2);
|
|
23773
23773
|
};
|
|
23774
23774
|
overridePropsRenamePath = function(fiber, oldPath, newPath) {
|
|
23775
23775
|
fiber.pendingProps = copyWithRename(
|
|
@@ -27007,21 +27007,6 @@ var SessionExpiredError = class extends Error {
|
|
|
27007
27007
|
this.name = "SessionExpiredError";
|
|
27008
27008
|
}
|
|
27009
27009
|
};
|
|
27010
|
-
function accessTokenSubject(credentials) {
|
|
27011
|
-
try {
|
|
27012
|
-
const parts = credentials.accessToken.split(".");
|
|
27013
|
-
if (parts.length !== 3) throw new Error();
|
|
27014
|
-
const payload = JSON.parse(
|
|
27015
|
-
Buffer.from(parts[1], "base64url").toString("utf8")
|
|
27016
|
-
);
|
|
27017
|
-
if (typeof payload.sub !== "string" || payload.sub.length === 0) {
|
|
27018
|
-
throw new Error();
|
|
27019
|
-
}
|
|
27020
|
-
return payload.sub;
|
|
27021
|
-
} catch {
|
|
27022
|
-
throw new Error("Glossa could not identify the signed-in account.");
|
|
27023
|
-
}
|
|
27024
|
-
}
|
|
27025
27010
|
function sessionExpiredError() {
|
|
27026
27011
|
return new SessionExpiredError();
|
|
27027
27012
|
}
|
|
@@ -28014,10 +27999,10 @@ function mergeDefs(...defs) {
|
|
|
28014
27999
|
function cloneDef(schema) {
|
|
28015
28000
|
return mergeDefs(schema._zod.def);
|
|
28016
28001
|
}
|
|
28017
|
-
function getElementAtPath(obj,
|
|
28018
|
-
if (!
|
|
28002
|
+
function getElementAtPath(obj, path14) {
|
|
28003
|
+
if (!path14)
|
|
28019
28004
|
return obj;
|
|
28020
|
-
return
|
|
28005
|
+
return path14.reduce((acc, key) => acc?.[key], obj);
|
|
28021
28006
|
}
|
|
28022
28007
|
function promiseAllObject(promisesObj) {
|
|
28023
28008
|
const keys = Object.keys(promisesObj);
|
|
@@ -28378,11 +28363,11 @@ function aborted(x, startIndex = 0) {
|
|
|
28378
28363
|
}
|
|
28379
28364
|
return false;
|
|
28380
28365
|
}
|
|
28381
|
-
function prefixIssues(
|
|
28366
|
+
function prefixIssues(path14, issues) {
|
|
28382
28367
|
return issues.map((iss) => {
|
|
28383
28368
|
var _a;
|
|
28384
28369
|
(_a = iss).path ?? (_a.path = []);
|
|
28385
|
-
iss.path.unshift(
|
|
28370
|
+
iss.path.unshift(path14);
|
|
28386
28371
|
return iss;
|
|
28387
28372
|
});
|
|
28388
28373
|
}
|
|
@@ -28544,7 +28529,7 @@ function formatError(error46, mapper = (issue2) => issue2.message) {
|
|
|
28544
28529
|
}
|
|
28545
28530
|
function treeifyError(error46, mapper = (issue2) => issue2.message) {
|
|
28546
28531
|
const result = { errors: [] };
|
|
28547
|
-
const processError = (error47,
|
|
28532
|
+
const processError = (error47, path14 = []) => {
|
|
28548
28533
|
var _a, _b;
|
|
28549
28534
|
for (const issue2 of error47.issues) {
|
|
28550
28535
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -28554,7 +28539,7 @@ function treeifyError(error46, mapper = (issue2) => issue2.message) {
|
|
|
28554
28539
|
} else if (issue2.code === "invalid_element") {
|
|
28555
28540
|
processError({ issues: issue2.issues }, issue2.path);
|
|
28556
28541
|
} else {
|
|
28557
|
-
const fullpath = [...
|
|
28542
|
+
const fullpath = [...path14, ...issue2.path];
|
|
28558
28543
|
if (fullpath.length === 0) {
|
|
28559
28544
|
result.errors.push(mapper(issue2));
|
|
28560
28545
|
continue;
|
|
@@ -28586,8 +28571,8 @@ function treeifyError(error46, mapper = (issue2) => issue2.message) {
|
|
|
28586
28571
|
}
|
|
28587
28572
|
function toDotPath(_path) {
|
|
28588
28573
|
const segs = [];
|
|
28589
|
-
const
|
|
28590
|
-
for (const seg of
|
|
28574
|
+
const path14 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
28575
|
+
for (const seg of path14) {
|
|
28591
28576
|
if (typeof seg === "number")
|
|
28592
28577
|
segs.push(`[${seg}]`);
|
|
28593
28578
|
else if (typeof seg === "symbol")
|
|
@@ -40271,6 +40256,10 @@ function parseInvocation(args) {
|
|
|
40271
40256
|
noOptions("Logout", options);
|
|
40272
40257
|
return { command };
|
|
40273
40258
|
}
|
|
40259
|
+
if (command === "unpair") {
|
|
40260
|
+
noOptions("Unpair", options);
|
|
40261
|
+
return { command };
|
|
40262
|
+
}
|
|
40274
40263
|
if (command === "update") return parseUpdate(options);
|
|
40275
40264
|
return parseWorkspace(args);
|
|
40276
40265
|
}
|
|
@@ -40322,7 +40311,7 @@ async function logoutFromGlossa(dependencies = {}) {
|
|
|
40322
40311
|
const issuer = dependencies.issuer ?? stored?.credentials.issuer;
|
|
40323
40312
|
await remove();
|
|
40324
40313
|
log(
|
|
40325
|
-
present ? "Signed out of Glossa." : "Already signed out of Glossa."
|
|
40314
|
+
present ? "Signed out of the Glossa account. Computer pairing stays active." : "Already signed out of the Glossa account. Computer pairing stays active."
|
|
40326
40315
|
);
|
|
40327
40316
|
const url2 = browserLogoutUrl(issuer ?? loadAuthConfig().issuer);
|
|
40328
40317
|
const opened = await browse(url2);
|
|
@@ -40381,6 +40370,12 @@ function loadRelayEndpoints(environment = process.env) {
|
|
|
40381
40370
|
function defaultDeviceName() {
|
|
40382
40371
|
return deviceNameSchema.parse(os2.hostname());
|
|
40383
40372
|
}
|
|
40373
|
+
var DevicePairingExpiredError = class extends Error {
|
|
40374
|
+
constructor() {
|
|
40375
|
+
super("The Glossa pairing code expired. Run Glossa again to get a new code.");
|
|
40376
|
+
this.name = "DevicePairingExpiredError";
|
|
40377
|
+
}
|
|
40378
|
+
};
|
|
40384
40379
|
function relayError(status, data) {
|
|
40385
40380
|
if (status === 401) return new Error("Glossa login was rejected. Sign in again when prompted.");
|
|
40386
40381
|
if (status === 403 && data.error === "account_disabled") {
|
|
@@ -40434,55 +40429,79 @@ async function listDevices(endpoints, credentials, fetchRequest = fetch) {
|
|
|
40434
40429
|
if (!response.ok) throw relayError(response.status, data);
|
|
40435
40430
|
return parseDevices2(data.devices);
|
|
40436
40431
|
}
|
|
40437
|
-
async function
|
|
40438
|
-
const
|
|
40439
|
-
|
|
40440
|
-
|
|
40441
|
-
|
|
40432
|
+
async function beginDevicePairing(endpoints, deviceName, fetchRequest = fetch) {
|
|
40433
|
+
const name = deviceNameSchema.parse(deviceName);
|
|
40434
|
+
const response = await fetchRequest(`${endpoints.relayOrigin}/v1/device-pairings`, {
|
|
40435
|
+
method: "POST",
|
|
40436
|
+
headers: { "content-type": "application/json" },
|
|
40437
|
+
body: JSON.stringify({ name, platform: `${process.platform}-${process.arch}` })
|
|
40438
|
+
});
|
|
40439
|
+
let data = {};
|
|
40440
|
+
try {
|
|
40441
|
+
data = await response.json();
|
|
40442
|
+
} catch {
|
|
40443
|
+
}
|
|
40444
|
+
if (!response.ok) {
|
|
40445
|
+
if (response.status === 429) {
|
|
40446
|
+
throw new Error("Glossa pairing is rate limited. Try again later.");
|
|
40447
|
+
}
|
|
40448
|
+
throw new Error(`The Glossa relay returned HTTP ${response.status}.`);
|
|
40449
|
+
}
|
|
40450
|
+
if (typeof data.pairing_id !== "string" || typeof data.user_code !== "string" || typeof data.pairing_secret !== "string" || typeof data.expires_at !== "string" || !Number.isInteger(data.poll_interval_ms) || data.poll_interval_ms < 1e3 || data.poll_interval_ms > 1e4 || !/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(data.pairing_id) || !/^[A-HJ-NP-Z2-9]{5}-[A-HJ-NP-Z2-9]{5}$/.test(data.user_code) || data.pairing_secret.length < 32 || data.pairing_secret.length > 128 || !Number.isFinite(Date.parse(data.expires_at))) {
|
|
40451
|
+
throw new Error("The Glossa relay returned an invalid pairing response.");
|
|
40452
|
+
}
|
|
40453
|
+
return {
|
|
40454
|
+
pairingId: data.pairing_id,
|
|
40455
|
+
userCode: data.user_code,
|
|
40456
|
+
pairingSecret: data.pairing_secret,
|
|
40457
|
+
expiresAt: data.expires_at,
|
|
40458
|
+
pollIntervalMs: data.poll_interval_ms
|
|
40459
|
+
};
|
|
40442
40460
|
}
|
|
40443
|
-
async function
|
|
40444
|
-
const
|
|
40445
|
-
|
|
40446
|
-
|
|
40447
|
-
const response = await fetchRequest(`${endpoints.relayOrigin}/v1/devices/enroll`, {
|
|
40461
|
+
async function completeDevicePairing(endpoints, pairing, fetchRequest = fetch) {
|
|
40462
|
+
const response = await fetchRequest(
|
|
40463
|
+
`${endpoints.relayOrigin}/v1/device-pairings/${encodeURIComponent(pairing.pairingId)}/complete`,
|
|
40464
|
+
{
|
|
40448
40465
|
method: "POST",
|
|
40449
|
-
headers: {
|
|
40450
|
-
|
|
40451
|
-
"content-type": "application/json"
|
|
40452
|
-
},
|
|
40453
|
-
body: JSON.stringify({ name, platform: `${process.platform}-${process.arch}` })
|
|
40454
|
-
});
|
|
40455
|
-
let data = {};
|
|
40456
|
-
try {
|
|
40457
|
-
data = await response.json();
|
|
40458
|
-
} catch {
|
|
40459
|
-
}
|
|
40460
|
-
if (response.status === 409 && data.error === "device_name_conflict" && attempt < 2) {
|
|
40461
|
-
const activeNames = new Set(
|
|
40462
|
-
(await listDevices(endpoints, credentials, fetchRequest)).filter((device) => device.revokedAt === null).map((device) => device.name)
|
|
40463
|
-
);
|
|
40464
|
-
for (let suffix = 2; ; suffix += 1) {
|
|
40465
|
-
const ending = `-${suffix}`;
|
|
40466
|
-
const candidate = `${baseName.slice(0, 80 - ending.length)}${ending}`;
|
|
40467
|
-
if (!activeNames.has(candidate)) {
|
|
40468
|
-
name = deviceNameSchema.parse(candidate);
|
|
40469
|
-
break;
|
|
40470
|
-
}
|
|
40471
|
-
}
|
|
40472
|
-
continue;
|
|
40473
|
-
}
|
|
40474
|
-
if (!response.ok) throw relayError(response.status, data);
|
|
40475
|
-
if (typeof data.device?.id !== "string" || typeof data.device.name !== "string" || typeof data.device_token !== "string") {
|
|
40476
|
-
throw new Error("The Glossa relay returned an invalid device enrollment response.");
|
|
40466
|
+
headers: { "content-type": "application/json" },
|
|
40467
|
+
body: JSON.stringify({ pairing_secret: pairing.pairingSecret })
|
|
40477
40468
|
}
|
|
40478
|
-
|
|
40479
|
-
|
|
40480
|
-
|
|
40481
|
-
|
|
40482
|
-
|
|
40483
|
-
|
|
40469
|
+
);
|
|
40470
|
+
let data = {};
|
|
40471
|
+
try {
|
|
40472
|
+
data = await response.json();
|
|
40473
|
+
} catch {
|
|
40474
|
+
}
|
|
40475
|
+
if (response.status === 202 && data.status === "authorization_pending") {
|
|
40476
|
+
return null;
|
|
40477
|
+
}
|
|
40478
|
+
if (response.status === 410 && data.error === "pairing_expired" || response.status === 404 && data.error === "pairing_not_found") {
|
|
40479
|
+
throw new DevicePairingExpiredError();
|
|
40480
|
+
}
|
|
40481
|
+
if (response.status === 429) {
|
|
40482
|
+
throw new Error("Glossa pairing is rate limited. Try again later.");
|
|
40483
|
+
}
|
|
40484
|
+
if (!response.ok) throw relayError(response.status, data);
|
|
40485
|
+
if (typeof data.device?.id !== "string" || typeof data.device.name !== "string" || typeof data.device_token !== "string") {
|
|
40486
|
+
throw new Error("The Glossa relay returned an invalid device pairing response.");
|
|
40487
|
+
}
|
|
40488
|
+
return {
|
|
40489
|
+
relayOrigin: endpoints.relayOrigin,
|
|
40490
|
+
deviceId: data.device.id,
|
|
40491
|
+
deviceName: data.device.name,
|
|
40492
|
+
token: data.device_token
|
|
40493
|
+
};
|
|
40494
|
+
}
|
|
40495
|
+
async function revokePairedDevice(endpoints, device, fetchRequest = fetch) {
|
|
40496
|
+
const response = await fetchRequest(`${endpoints.relayOrigin}/device`, {
|
|
40497
|
+
method: "DELETE",
|
|
40498
|
+
headers: { authorization: `Device ${device.token}` }
|
|
40499
|
+
});
|
|
40500
|
+
if (response.ok || response.status === 401 || response.status === 404) return;
|
|
40501
|
+
if (response.status === 429) {
|
|
40502
|
+
throw new Error("Glossa device authentication is rate limited. Try again later.");
|
|
40484
40503
|
}
|
|
40485
|
-
throw new Error(
|
|
40504
|
+
throw new Error(`The Glossa relay returned HTTP ${response.status}.`);
|
|
40486
40505
|
}
|
|
40487
40506
|
async function revokeDevice(endpoints, credentials, deviceId, fetchRequest = fetch) {
|
|
40488
40507
|
const response = await fetchRequest(`${endpoints.relayOrigin}/v1/devices/${encodeURIComponent(deviceId)}`, {
|
|
@@ -47670,8 +47689,8 @@ function Text({ color, backgroundColor, dimColor = false, bold = false, italic =
|
|
|
47670
47689
|
}
|
|
47671
47690
|
|
|
47672
47691
|
// ../../node_modules/ink/build/components/ErrorOverview.js
|
|
47673
|
-
var cleanupPath = (
|
|
47674
|
-
return
|
|
47692
|
+
var cleanupPath = (path14) => {
|
|
47693
|
+
return path14?.replace(`file://${cwd()}/`, "");
|
|
47675
47694
|
};
|
|
47676
47695
|
var stackUtils = new import_stack_utils.default({
|
|
47677
47696
|
cwd: cwd(),
|
|
@@ -47845,7 +47864,7 @@ function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, ex
|
|
|
47845
47864
|
for (const subscriber of animationSubscribersRef.current.values()) {
|
|
47846
47865
|
nextDueTime = Math.min(nextDueTime, subscriber.nextDueTime);
|
|
47847
47866
|
}
|
|
47848
|
-
const
|
|
47867
|
+
const delay6 = Math.max(0, nextDueTime - performance.now());
|
|
47849
47868
|
animationTimerRef.current = setTimeout(() => {
|
|
47850
47869
|
animationTimerRef.current = void 0;
|
|
47851
47870
|
const currentTime = performance.now();
|
|
@@ -47859,7 +47878,7 @@ function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, ex
|
|
|
47859
47878
|
subscriber.nextDueTime = subscriber.startTime + elapsedFrames * subscriber.interval;
|
|
47860
47879
|
}
|
|
47861
47880
|
scheduleAnimationTick();
|
|
47862
|
-
},
|
|
47881
|
+
}, delay6);
|
|
47863
47882
|
}, [clearAnimationTimer]);
|
|
47864
47883
|
const animationSubscribe = (0, import_react15.useCallback)((callback, interval) => {
|
|
47865
47884
|
const startTime = performance.now();
|
|
@@ -49799,7 +49818,7 @@ function connectHintStore(directory = configDirectory()) {
|
|
|
49799
49818
|
}
|
|
49800
49819
|
async function announceConnectHint(store3, log) {
|
|
49801
49820
|
if (await store3.exists()) return false;
|
|
49802
|
-
log(`Next:
|
|
49821
|
+
log(`Next: return to ChatGPT and use Glossa. If it is not connected yet, follow ${CONNECT_HINT_URL}.`);
|
|
49803
49822
|
await store3.mark();
|
|
49804
49823
|
return true;
|
|
49805
49824
|
}
|
|
@@ -49845,11 +49864,298 @@ async function deleteDeviceCredential() {
|
|
|
49845
49864
|
await store2.delete();
|
|
49846
49865
|
}
|
|
49847
49866
|
|
|
49867
|
+
// src/device-pairing-lock.ts
|
|
49868
|
+
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
49869
|
+
import { mkdir as mkdir4, readFile as readFile3, rm as rm3, stat, writeFile as writeFile4 } from "node:fs/promises";
|
|
49870
|
+
import path6 from "node:path";
|
|
49871
|
+
import { setTimeout as delay2 } from "node:timers/promises";
|
|
49872
|
+
|
|
49873
|
+
// src/update-lock.ts
|
|
49874
|
+
import { randomUUID } from "node:crypto";
|
|
49875
|
+
import { mkdir as mkdir3, readdir, rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
|
|
49876
|
+
import path5 from "node:path";
|
|
49877
|
+
var UPDATE_SUFFIX = ".update";
|
|
49878
|
+
var SESSION_SUFFIX = ".session";
|
|
49879
|
+
function updateRuntimeDirectory() {
|
|
49880
|
+
return path5.join(configDirectory(), "runtime");
|
|
49881
|
+
}
|
|
49882
|
+
function processIsAlive(pid) {
|
|
49883
|
+
if (!Number.isSafeInteger(pid) || pid <= 0) return false;
|
|
49884
|
+
try {
|
|
49885
|
+
process.kill(pid, 0);
|
|
49886
|
+
return true;
|
|
49887
|
+
} catch (error46) {
|
|
49888
|
+
return error46.code === "EPERM";
|
|
49889
|
+
}
|
|
49890
|
+
}
|
|
49891
|
+
function leasePid(name, suffix) {
|
|
49892
|
+
if (!name.endsWith(suffix)) return null;
|
|
49893
|
+
const separator = name.indexOf("-");
|
|
49894
|
+
if (separator <= 0) return null;
|
|
49895
|
+
const pid = Number(name.slice(0, separator));
|
|
49896
|
+
return Number.isSafeInteger(pid) && pid > 0 ? pid : null;
|
|
49897
|
+
}
|
|
49898
|
+
async function activeLeaseFiles(directory, suffix) {
|
|
49899
|
+
let entries;
|
|
49900
|
+
try {
|
|
49901
|
+
entries = await readdir(directory, { withFileTypes: true });
|
|
49902
|
+
} catch (error46) {
|
|
49903
|
+
if (error46.code === "ENOENT") return [];
|
|
49904
|
+
throw error46;
|
|
49905
|
+
}
|
|
49906
|
+
const active = [];
|
|
49907
|
+
for (const entry of entries) {
|
|
49908
|
+
if (!entry.isFile() || !entry.name.endsWith(suffix)) continue;
|
|
49909
|
+
const pid = leasePid(entry.name, suffix);
|
|
49910
|
+
const file2 = path5.join(directory, entry.name);
|
|
49911
|
+
if (pid !== null && processIsAlive(pid)) active.push(file2);
|
|
49912
|
+
else await rm2(file2, { force: true });
|
|
49913
|
+
}
|
|
49914
|
+
return active;
|
|
49915
|
+
}
|
|
49916
|
+
async function updateLockIsActive(directory) {
|
|
49917
|
+
return (await activeLeaseFiles(directory, UPDATE_SUFFIX)).length > 0;
|
|
49918
|
+
}
|
|
49919
|
+
async function activeSessionFiles(directory) {
|
|
49920
|
+
return await activeLeaseFiles(directory, SESSION_SUFFIX);
|
|
49921
|
+
}
|
|
49922
|
+
async function createUpdateLock(directory) {
|
|
49923
|
+
await mkdir3(directory, { recursive: true, mode: 448 });
|
|
49924
|
+
const lockFile = path5.join(
|
|
49925
|
+
directory,
|
|
49926
|
+
`${process.pid}-${randomUUID()}${UPDATE_SUFFIX}`
|
|
49927
|
+
);
|
|
49928
|
+
const content = `${JSON.stringify({ pid: process.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString() })}
|
|
49929
|
+
`;
|
|
49930
|
+
await writeFile3(lockFile, content, {
|
|
49931
|
+
encoding: "utf8",
|
|
49932
|
+
flag: "wx",
|
|
49933
|
+
mode: 384
|
|
49934
|
+
});
|
|
49935
|
+
try {
|
|
49936
|
+
const contenders = await activeLeaseFiles(directory, UPDATE_SUFFIX);
|
|
49937
|
+
if (contenders.some((file2) => file2 !== lockFile)) {
|
|
49938
|
+
throw new Error("Another Glossa update is already running.");
|
|
49939
|
+
}
|
|
49940
|
+
return lockFile;
|
|
49941
|
+
} catch (error46) {
|
|
49942
|
+
await rm2(lockFile, { force: true });
|
|
49943
|
+
throw error46;
|
|
49944
|
+
}
|
|
49945
|
+
}
|
|
49946
|
+
async function withUpdateLease(action, directory = updateRuntimeDirectory()) {
|
|
49947
|
+
const lockFile = await createUpdateLock(directory);
|
|
49948
|
+
try {
|
|
49949
|
+
if ((await activeSessionFiles(directory)).length > 0) {
|
|
49950
|
+
throw new Error("Disconnect every running Glossa workspace before updating.");
|
|
49951
|
+
}
|
|
49952
|
+
return await action();
|
|
49953
|
+
} finally {
|
|
49954
|
+
await rm2(lockFile, { force: true });
|
|
49955
|
+
}
|
|
49956
|
+
}
|
|
49957
|
+
async function withWorkspaceLease(action, directory = updateRuntimeDirectory()) {
|
|
49958
|
+
await mkdir3(directory, { recursive: true, mode: 448 });
|
|
49959
|
+
if (await updateLockIsActive(directory)) {
|
|
49960
|
+
throw new Error("Glossa is updating. Run this workspace again after the update finishes.");
|
|
49961
|
+
}
|
|
49962
|
+
const leaseFile = path5.join(
|
|
49963
|
+
directory,
|
|
49964
|
+
`${process.pid}-${randomUUID()}${SESSION_SUFFIX}`
|
|
49965
|
+
);
|
|
49966
|
+
await writeFile3(
|
|
49967
|
+
leaseFile,
|
|
49968
|
+
`${JSON.stringify({ pid: process.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString() })}
|
|
49969
|
+
`,
|
|
49970
|
+
{ encoding: "utf8", flag: "wx", mode: 384 }
|
|
49971
|
+
);
|
|
49972
|
+
try {
|
|
49973
|
+
if (await updateLockIsActive(directory)) {
|
|
49974
|
+
throw new Error("Glossa is updating. Run this workspace again after the update finishes.");
|
|
49975
|
+
}
|
|
49976
|
+
return await action();
|
|
49977
|
+
} finally {
|
|
49978
|
+
await rm2(leaseFile, { force: true });
|
|
49979
|
+
}
|
|
49980
|
+
}
|
|
49981
|
+
|
|
49982
|
+
// src/device-pairing-lock.ts
|
|
49983
|
+
var DEVICE_PAIRING_LOCK = "device-pairing.lock";
|
|
49984
|
+
var DEVICE_PAIRING_LOCK_GUARD = "device-pairing.lock.guard";
|
|
49985
|
+
var DEVICE_PAIRING_LOCK_POLL_MS = 100;
|
|
49986
|
+
var DEVICE_PAIRING_LOCK_MAX_AGE_MS = 6 * 6e4;
|
|
49987
|
+
var DEVICE_PAIRING_GUARD_MAX_AGE_MS = 3e4;
|
|
49988
|
+
function lockPath(directory) {
|
|
49989
|
+
return path6.join(directory, DEVICE_PAIRING_LOCK);
|
|
49990
|
+
}
|
|
49991
|
+
function guardPath(directory) {
|
|
49992
|
+
return path6.join(directory, DEVICE_PAIRING_LOCK_GUARD);
|
|
49993
|
+
}
|
|
49994
|
+
function newOwner() {
|
|
49995
|
+
return {
|
|
49996
|
+
pid: process.pid,
|
|
49997
|
+
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
49998
|
+
token: randomUUID2()
|
|
49999
|
+
};
|
|
50000
|
+
}
|
|
50001
|
+
async function readOwner(file2) {
|
|
50002
|
+
try {
|
|
50003
|
+
const value = JSON.parse(await readFile3(file2, "utf8"));
|
|
50004
|
+
if (!Number.isSafeInteger(value.pid) || typeof value.startedAt !== "string" || !Number.isFinite(Date.parse(value.startedAt)) || typeof value.token !== "string" || value.token.length === 0) {
|
|
50005
|
+
return void 0;
|
|
50006
|
+
}
|
|
50007
|
+
return value;
|
|
50008
|
+
} catch (error46) {
|
|
50009
|
+
if (error46.code === "ENOENT") return void 0;
|
|
50010
|
+
return void 0;
|
|
50011
|
+
}
|
|
50012
|
+
}
|
|
50013
|
+
async function lockIsStale(file2, maxAgeMs) {
|
|
50014
|
+
const owner = await readOwner(file2);
|
|
50015
|
+
if (!owner) {
|
|
50016
|
+
try {
|
|
50017
|
+
const metadata = await stat(file2);
|
|
50018
|
+
return Date.now() - metadata.mtimeMs > maxAgeMs;
|
|
50019
|
+
} catch (error46) {
|
|
50020
|
+
if (error46.code === "ENOENT") return false;
|
|
50021
|
+
return true;
|
|
50022
|
+
}
|
|
50023
|
+
}
|
|
50024
|
+
const ageMs = Date.now() - Date.parse(owner.startedAt);
|
|
50025
|
+
return ageMs > maxAgeMs || !processIsAlive(owner.pid);
|
|
50026
|
+
}
|
|
50027
|
+
async function acquireGuard(directory, signal) {
|
|
50028
|
+
const file2 = guardPath(directory);
|
|
50029
|
+
for (; ; ) {
|
|
50030
|
+
signal?.throwIfAborted();
|
|
50031
|
+
const owner = newOwner();
|
|
50032
|
+
try {
|
|
50033
|
+
await writeFile4(file2, `${JSON.stringify(owner)}
|
|
50034
|
+
`, {
|
|
50035
|
+
encoding: "utf8",
|
|
50036
|
+
flag: "wx",
|
|
50037
|
+
mode: 384
|
|
50038
|
+
});
|
|
50039
|
+
return { file: file2, token: owner.token };
|
|
50040
|
+
} catch (error46) {
|
|
50041
|
+
if (error46.code !== "EEXIST") throw error46;
|
|
50042
|
+
}
|
|
50043
|
+
if (await lockIsStale(file2, DEVICE_PAIRING_GUARD_MAX_AGE_MS)) {
|
|
50044
|
+
await rm3(file2, { force: true });
|
|
50045
|
+
continue;
|
|
50046
|
+
}
|
|
50047
|
+
await delay2(
|
|
50048
|
+
DEVICE_PAIRING_LOCK_POLL_MS,
|
|
50049
|
+
void 0,
|
|
50050
|
+
signal ? { signal } : void 0
|
|
50051
|
+
);
|
|
50052
|
+
}
|
|
50053
|
+
}
|
|
50054
|
+
async function removeOwnedLock(file2, token) {
|
|
50055
|
+
const owner = await readOwner(file2);
|
|
50056
|
+
if (owner?.token === token) await rm3(file2, { force: true });
|
|
50057
|
+
}
|
|
50058
|
+
async function withGuard(directory, action, signal) {
|
|
50059
|
+
const guard = await acquireGuard(directory, signal);
|
|
50060
|
+
try {
|
|
50061
|
+
return await action();
|
|
50062
|
+
} finally {
|
|
50063
|
+
await removeOwnedLock(guard.file, guard.token);
|
|
50064
|
+
}
|
|
50065
|
+
}
|
|
50066
|
+
async function acquireDevicePairingLock(directory, signal) {
|
|
50067
|
+
await mkdir4(directory, { recursive: true, mode: 448 });
|
|
50068
|
+
const file2 = lockPath(directory);
|
|
50069
|
+
for (; ; ) {
|
|
50070
|
+
signal?.throwIfAborted();
|
|
50071
|
+
const owner = newOwner();
|
|
50072
|
+
try {
|
|
50073
|
+
await writeFile4(file2, `${JSON.stringify(owner)}
|
|
50074
|
+
`, {
|
|
50075
|
+
encoding: "utf8",
|
|
50076
|
+
flag: "wx",
|
|
50077
|
+
mode: 384
|
|
50078
|
+
});
|
|
50079
|
+
return { file: file2, token: owner.token };
|
|
50080
|
+
} catch (error46) {
|
|
50081
|
+
if (error46.code !== "EEXIST") throw error46;
|
|
50082
|
+
}
|
|
50083
|
+
const reclaimed = await withGuard(
|
|
50084
|
+
directory,
|
|
50085
|
+
async () => {
|
|
50086
|
+
if (!await lockIsStale(file2, DEVICE_PAIRING_LOCK_MAX_AGE_MS)) {
|
|
50087
|
+
return false;
|
|
50088
|
+
}
|
|
50089
|
+
await rm3(file2, { force: true });
|
|
50090
|
+
return true;
|
|
50091
|
+
},
|
|
50092
|
+
signal
|
|
50093
|
+
);
|
|
50094
|
+
if (reclaimed) continue;
|
|
50095
|
+
await delay2(
|
|
50096
|
+
DEVICE_PAIRING_LOCK_POLL_MS,
|
|
50097
|
+
void 0,
|
|
50098
|
+
signal ? { signal } : void 0
|
|
50099
|
+
);
|
|
50100
|
+
}
|
|
50101
|
+
}
|
|
50102
|
+
async function withDevicePairingLease(action, signal, directory = updateRuntimeDirectory()) {
|
|
50103
|
+
const lock = await acquireDevicePairingLock(directory, signal);
|
|
50104
|
+
try {
|
|
50105
|
+
return await action();
|
|
50106
|
+
} finally {
|
|
50107
|
+
await withGuard(directory, async () => await removeOwnedLock(lock.file, lock.token));
|
|
50108
|
+
}
|
|
50109
|
+
}
|
|
50110
|
+
|
|
50111
|
+
// src/device-pairing.ts
|
|
50112
|
+
import { setTimeout as delay3 } from "node:timers/promises";
|
|
50113
|
+
async function defaultWait(milliseconds, signal) {
|
|
50114
|
+
await delay3(milliseconds, void 0, signal ? { signal } : void 0);
|
|
50115
|
+
}
|
|
50116
|
+
function canceledError2() {
|
|
50117
|
+
return new Error("Pairing canceled.");
|
|
50118
|
+
}
|
|
50119
|
+
async function pairDevice(endpoints, signal, dependencies = {}) {
|
|
50120
|
+
const begin = dependencies.beginDevicePairing ?? beginDevicePairing;
|
|
50121
|
+
const complete = dependencies.completeDevicePairing ?? completeDevicePairing;
|
|
50122
|
+
const name = dependencies.defaultDeviceName ?? defaultDeviceName;
|
|
50123
|
+
const wait = dependencies.wait ?? defaultWait;
|
|
50124
|
+
const now = dependencies.now ?? Date.now;
|
|
50125
|
+
const log = dependencies.log ?? console.log;
|
|
50126
|
+
const baseFetch = dependencies.fetch ?? fetch;
|
|
50127
|
+
const fetchRequest = signal ? async (input, init) => await baseFetch(input, { ...init, signal }) : baseFetch;
|
|
50128
|
+
try {
|
|
50129
|
+
signal?.throwIfAborted();
|
|
50130
|
+
const challenge = await begin(endpoints, name(), fetchRequest);
|
|
50131
|
+
log("This computer needs to be paired with Glossa.");
|
|
50132
|
+
log("In ChatGPT, send this message:");
|
|
50133
|
+
log(`@Glossa pair ${challenge.userCode}`);
|
|
50134
|
+
log("Keep this terminal open. The pairing code expires in 5 minutes.");
|
|
50135
|
+
const expiresAt = Date.parse(challenge.expiresAt);
|
|
50136
|
+
while (now() < expiresAt) {
|
|
50137
|
+
signal?.throwIfAborted();
|
|
50138
|
+
await wait(challenge.pollIntervalMs, signal);
|
|
50139
|
+
signal?.throwIfAborted();
|
|
50140
|
+
const paired = await complete(endpoints, challenge, fetchRequest);
|
|
50141
|
+
if (!paired) continue;
|
|
50142
|
+
log(`Paired with Glossa as ${paired.deviceName}.`);
|
|
50143
|
+
return paired;
|
|
50144
|
+
}
|
|
50145
|
+
throw new Error("The Glossa pairing code expired. Run Glossa again to get a new code.");
|
|
50146
|
+
} catch (error46) {
|
|
50147
|
+
if (signal?.aborted || error46 instanceof Error && error46.name === "AbortError") {
|
|
50148
|
+
throw canceledError2();
|
|
50149
|
+
}
|
|
50150
|
+
throw error46;
|
|
50151
|
+
}
|
|
50152
|
+
}
|
|
50153
|
+
|
|
49848
50154
|
// src/worker/command-service.ts
|
|
49849
50155
|
import { spawn as spawn2 } from "node:child_process";
|
|
49850
|
-
import { randomUUID } from "node:crypto";
|
|
50156
|
+
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
49851
50157
|
import { StringDecoder } from "node:string_decoder";
|
|
49852
|
-
import { setTimeout as
|
|
50158
|
+
import { setTimeout as delay4 } from "node:timers/promises";
|
|
49853
50159
|
|
|
49854
50160
|
// src/worker/errors.ts
|
|
49855
50161
|
var WorkerError = class extends Error {
|
|
@@ -49964,7 +50270,7 @@ async function waitForChange(record2, afterSequence, waitMs) {
|
|
|
49964
50270
|
try {
|
|
49965
50271
|
await Promise.race([
|
|
49966
50272
|
change,
|
|
49967
|
-
|
|
50273
|
+
delay4(waitMs, void 0, { signal: waitController.signal })
|
|
49968
50274
|
]);
|
|
49969
50275
|
} finally {
|
|
49970
50276
|
record2.changeWaiters.delete(changed);
|
|
@@ -50135,7 +50441,7 @@ async function terminateProcessTree(child) {
|
|
|
50135
50441
|
} catch {
|
|
50136
50442
|
child.kill("SIGTERM");
|
|
50137
50443
|
}
|
|
50138
|
-
await
|
|
50444
|
+
await delay4(2e3);
|
|
50139
50445
|
if (child.exitCode === null) {
|
|
50140
50446
|
try {
|
|
50141
50447
|
process.kill(-child.pid, "SIGKILL");
|
|
@@ -50228,7 +50534,7 @@ var CommandService = class {
|
|
|
50228
50534
|
const completion = new Promise((resolve) => {
|
|
50229
50535
|
complete = resolve;
|
|
50230
50536
|
});
|
|
50231
|
-
const id =
|
|
50537
|
+
const id = randomUUID3();
|
|
50232
50538
|
const record2 = {
|
|
50233
50539
|
id,
|
|
50234
50540
|
child,
|
|
@@ -50300,7 +50606,7 @@ var CommandService = class {
|
|
|
50300
50606
|
try {
|
|
50301
50607
|
await Promise.race([
|
|
50302
50608
|
record2.completion,
|
|
50303
|
-
|
|
50609
|
+
delay4(waitMs, void 0, { signal: waitController.signal })
|
|
50304
50610
|
]);
|
|
50305
50611
|
} finally {
|
|
50306
50612
|
waitController.abort();
|
|
@@ -50326,7 +50632,7 @@ var CommandService = class {
|
|
|
50326
50632
|
try {
|
|
50327
50633
|
await Promise.race([
|
|
50328
50634
|
record2.completion,
|
|
50329
|
-
|
|
50635
|
+
delay4(waitMs, void 0, { signal: waitController.signal })
|
|
50330
50636
|
]);
|
|
50331
50637
|
} finally {
|
|
50332
50638
|
waitController.abort();
|
|
@@ -50428,29 +50734,29 @@ var CommandService = class {
|
|
|
50428
50734
|
};
|
|
50429
50735
|
|
|
50430
50736
|
// src/worker/file-service.ts
|
|
50431
|
-
import { createHash, randomUUID as
|
|
50737
|
+
import { createHash, randomUUID as randomUUID4 } from "node:crypto";
|
|
50432
50738
|
import {
|
|
50433
50739
|
chmod as chmod2,
|
|
50434
50740
|
link as link2,
|
|
50435
50741
|
lstat as lstat2,
|
|
50436
|
-
mkdir as
|
|
50742
|
+
mkdir as mkdir5,
|
|
50437
50743
|
opendir,
|
|
50438
50744
|
open,
|
|
50439
|
-
readFile as
|
|
50745
|
+
readFile as readFile4,
|
|
50440
50746
|
rename,
|
|
50441
|
-
rm as
|
|
50747
|
+
rm as rm4,
|
|
50442
50748
|
rmdir,
|
|
50443
|
-
stat as
|
|
50444
|
-
writeFile as
|
|
50749
|
+
stat as stat3,
|
|
50750
|
+
writeFile as writeFile5
|
|
50445
50751
|
} from "node:fs/promises";
|
|
50446
|
-
import
|
|
50752
|
+
import path8 from "node:path";
|
|
50447
50753
|
import { performance as performance2 } from "node:perf_hooks";
|
|
50448
50754
|
import { StringDecoder as StringDecoder2 } from "node:string_decoder";
|
|
50449
50755
|
|
|
50450
50756
|
// src/worker/path-policy.ts
|
|
50451
|
-
import { lstat, realpath, stat } from "node:fs/promises";
|
|
50757
|
+
import { lstat, realpath, stat as stat2 } from "node:fs/promises";
|
|
50452
50758
|
import os5 from "node:os";
|
|
50453
|
-
import
|
|
50759
|
+
import path7 from "node:path";
|
|
50454
50760
|
function samePath(left, right) {
|
|
50455
50761
|
return process.platform === "win32" ? left.toLowerCase() === right.toLowerCase() : left === right;
|
|
50456
50762
|
}
|
|
@@ -50462,15 +50768,15 @@ function accountHomeDirectory() {
|
|
|
50462
50768
|
}
|
|
50463
50769
|
}
|
|
50464
50770
|
function isWithin(root, candidate) {
|
|
50465
|
-
const relative =
|
|
50466
|
-
return relative === "" || !relative.startsWith(`..${
|
|
50771
|
+
const relative = path7.relative(root, candidate);
|
|
50772
|
+
return relative === "" || !relative.startsWith(`..${path7.sep}`) && relative !== ".." && !path7.isAbsolute(relative);
|
|
50467
50773
|
}
|
|
50468
50774
|
function validateRelativePath(value) {
|
|
50469
50775
|
if (value.includes("\0")) {
|
|
50470
50776
|
throw new WorkerError("invalid_path", "Paths cannot contain null bytes.");
|
|
50471
50777
|
}
|
|
50472
50778
|
const explicitNativePosixPath = process.platform !== "win32" && value.startsWith("./");
|
|
50473
|
-
if (
|
|
50779
|
+
if (path7.isAbsolute(value) || path7.posix.isAbsolute(value) || !explicitNativePosixPath && path7.win32.isAbsolute(value)) {
|
|
50474
50780
|
throw new WorkerError("absolute_path", "Absolute paths are not allowed.");
|
|
50475
50781
|
}
|
|
50476
50782
|
const segments = explicitNativePosixPath ? value.split(/\/+/).filter(Boolean) : value.split(/[\\/]+/);
|
|
@@ -50480,20 +50786,20 @@ function validateRelativePath(value) {
|
|
|
50480
50786
|
return value === "" ? "." : value;
|
|
50481
50787
|
}
|
|
50482
50788
|
async function canonicalizeRoot(candidate) {
|
|
50483
|
-
const root = await realpath(
|
|
50789
|
+
const root = await realpath(path7.resolve(candidate)).catch((error46) => {
|
|
50484
50790
|
if (error46.code === "ENOENT") {
|
|
50485
50791
|
throw new WorkerError("root_not_found", "The workspace directory does not exist.");
|
|
50486
50792
|
}
|
|
50487
50793
|
throw error46;
|
|
50488
50794
|
});
|
|
50489
|
-
const rootStat = await
|
|
50795
|
+
const rootStat = await stat2(root);
|
|
50490
50796
|
if (!rootStat.isDirectory()) {
|
|
50491
50797
|
throw new WorkerError("root_not_directory", "The exposed root must be a directory.");
|
|
50492
50798
|
}
|
|
50493
|
-
const filesystemRoot =
|
|
50799
|
+
const filesystemRoot = path7.parse(root).root;
|
|
50494
50800
|
const homes = await Promise.all(
|
|
50495
50801
|
[os5.homedir(), accountHomeDirectory()].map(
|
|
50496
|
-
async (home) => await realpath(home).catch(() =>
|
|
50802
|
+
async (home) => await realpath(home).catch(() => path7.resolve(home))
|
|
50497
50803
|
)
|
|
50498
50804
|
);
|
|
50499
50805
|
const isHomeDirectory = homes.some((home) => samePath(root, home));
|
|
@@ -50529,7 +50835,7 @@ var PathPolicy = class _PathPolicy {
|
|
|
50529
50835
|
return canonical;
|
|
50530
50836
|
}
|
|
50531
50837
|
async resolveDiscoveredExisting(candidate) {
|
|
50532
|
-
const lexical =
|
|
50838
|
+
const lexical = path7.resolve(candidate);
|
|
50533
50839
|
if (!isWithin(this.root, lexical)) {
|
|
50534
50840
|
throw new WorkerError("path_escape", "The requested path escapes the exposed root.");
|
|
50535
50841
|
}
|
|
@@ -50547,7 +50853,7 @@ var PathPolicy = class _PathPolicy {
|
|
|
50547
50853
|
}
|
|
50548
50854
|
async resolveWritableFile(relativePath) {
|
|
50549
50855
|
const lexical = this.resolveLexical(relativePath);
|
|
50550
|
-
const parent =
|
|
50856
|
+
const parent = path7.dirname(lexical);
|
|
50551
50857
|
await this.rejectLinkedComponents(parent);
|
|
50552
50858
|
const canonicalParent = await realpath(parent).catch((error46) => {
|
|
50553
50859
|
if (error46.code === "ENOENT") {
|
|
@@ -50558,7 +50864,7 @@ var PathPolicy = class _PathPolicy {
|
|
|
50558
50864
|
if (!isWithin(this.root, canonicalParent)) {
|
|
50559
50865
|
throw new WorkerError("path_escape", "The destination escapes the exposed root.");
|
|
50560
50866
|
}
|
|
50561
|
-
if (!(await
|
|
50867
|
+
if (!(await stat2(canonicalParent)).isDirectory()) {
|
|
50562
50868
|
throw new WorkerError("not_directory", "The destination parent is not a directory.");
|
|
50563
50869
|
}
|
|
50564
50870
|
try {
|
|
@@ -50578,7 +50884,7 @@ var PathPolicy = class _PathPolicy {
|
|
|
50578
50884
|
if (error46 instanceof WorkerError) throw error46;
|
|
50579
50885
|
if (error46.code !== "ENOENT") throw error46;
|
|
50580
50886
|
}
|
|
50581
|
-
return
|
|
50887
|
+
return path7.join(canonicalParent, path7.basename(lexical));
|
|
50582
50888
|
}
|
|
50583
50889
|
async resolveWritableDirectory(relativePath, recursive) {
|
|
50584
50890
|
const lexical = this.resolveLexical(relativePath);
|
|
@@ -50601,7 +50907,7 @@ var PathPolicy = class _PathPolicy {
|
|
|
50601
50907
|
if (error46.code !== "ENOENT") throw error46;
|
|
50602
50908
|
}
|
|
50603
50909
|
if (!recursive) {
|
|
50604
|
-
const parent =
|
|
50910
|
+
const parent = path7.dirname(lexical);
|
|
50605
50911
|
await this.rejectLinkedComponents(parent);
|
|
50606
50912
|
const canonicalParent = await realpath(parent).catch(
|
|
50607
50913
|
(error46) => {
|
|
@@ -50617,15 +50923,15 @@ var PathPolicy = class _PathPolicy {
|
|
|
50617
50923
|
if (!isWithin(this.root, canonicalParent)) {
|
|
50618
50924
|
throw new WorkerError("path_escape", "The destination escapes the exposed root.");
|
|
50619
50925
|
}
|
|
50620
|
-
if (!(await
|
|
50926
|
+
if (!(await stat2(canonicalParent)).isDirectory()) {
|
|
50621
50927
|
throw new WorkerError("not_directory", "The destination parent is not a directory.");
|
|
50622
50928
|
}
|
|
50623
50929
|
return {
|
|
50624
|
-
target:
|
|
50930
|
+
target: path7.join(canonicalParent, path7.basename(lexical)),
|
|
50625
50931
|
exists: false
|
|
50626
50932
|
};
|
|
50627
50933
|
}
|
|
50628
|
-
let existingAncestor =
|
|
50934
|
+
let existingAncestor = path7.dirname(lexical);
|
|
50629
50935
|
while (!samePath(existingAncestor, this.root)) {
|
|
50630
50936
|
try {
|
|
50631
50937
|
await this.rejectLinkedComponents(existingAncestor);
|
|
@@ -50633,13 +50939,13 @@ var PathPolicy = class _PathPolicy {
|
|
|
50633
50939
|
if (!isWithin(this.root, canonicalAncestor)) {
|
|
50634
50940
|
throw new WorkerError("path_escape", "The destination escapes the exposed root.");
|
|
50635
50941
|
}
|
|
50636
|
-
if (!(await
|
|
50942
|
+
if (!(await stat2(canonicalAncestor)).isDirectory()) {
|
|
50637
50943
|
throw new WorkerError("not_directory", "The destination parent is not a directory.");
|
|
50638
50944
|
}
|
|
50639
50945
|
return {
|
|
50640
|
-
target:
|
|
50946
|
+
target: path7.join(
|
|
50641
50947
|
canonicalAncestor,
|
|
50642
|
-
|
|
50948
|
+
path7.relative(existingAncestor, lexical)
|
|
50643
50949
|
),
|
|
50644
50950
|
exists: false
|
|
50645
50951
|
};
|
|
@@ -50647,13 +50953,13 @@ var PathPolicy = class _PathPolicy {
|
|
|
50647
50953
|
if (error46 instanceof WorkerError) throw error46;
|
|
50648
50954
|
if (error46.code !== "ENOENT") throw error46;
|
|
50649
50955
|
}
|
|
50650
|
-
existingAncestor =
|
|
50956
|
+
existingAncestor = path7.dirname(existingAncestor);
|
|
50651
50957
|
}
|
|
50652
50958
|
return { target: lexical, exists: false };
|
|
50653
50959
|
}
|
|
50654
50960
|
async resolveVacantPath(relativePath) {
|
|
50655
50961
|
const lexical = this.resolveLexical(relativePath);
|
|
50656
|
-
const parent =
|
|
50962
|
+
const parent = path7.dirname(lexical);
|
|
50657
50963
|
await this.rejectLinkedComponents(parent);
|
|
50658
50964
|
const canonicalParent = await realpath(parent).catch(
|
|
50659
50965
|
(error46) => {
|
|
@@ -50669,7 +50975,7 @@ var PathPolicy = class _PathPolicy {
|
|
|
50669
50975
|
if (!isWithin(this.root, canonicalParent)) {
|
|
50670
50976
|
throw new WorkerError("path_escape", "The destination escapes the exposed root.");
|
|
50671
50977
|
}
|
|
50672
|
-
if (!(await
|
|
50978
|
+
if (!(await stat2(canonicalParent)).isDirectory()) {
|
|
50673
50979
|
throw new WorkerError("not_directory", "The destination parent is not a directory.");
|
|
50674
50980
|
}
|
|
50675
50981
|
try {
|
|
@@ -50682,11 +50988,11 @@ var PathPolicy = class _PathPolicy {
|
|
|
50682
50988
|
if (error46 instanceof WorkerError) throw error46;
|
|
50683
50989
|
if (error46.code !== "ENOENT") throw error46;
|
|
50684
50990
|
}
|
|
50685
|
-
return
|
|
50991
|
+
return path7.join(canonicalParent, path7.basename(lexical));
|
|
50686
50992
|
}
|
|
50687
50993
|
resolveLexical(relativePath) {
|
|
50688
50994
|
const validated = validateRelativePath(relativePath);
|
|
50689
|
-
const candidate =
|
|
50995
|
+
const candidate = path7.resolve(this.root, validated);
|
|
50690
50996
|
if (!isWithin(this.root, candidate)) {
|
|
50691
50997
|
throw new WorkerError("path_escape", "The requested path escapes the exposed root.");
|
|
50692
50998
|
}
|
|
@@ -50696,11 +51002,11 @@ var PathPolicy = class _PathPolicy {
|
|
|
50696
51002
|
if (!isWithin(this.root, candidate)) {
|
|
50697
51003
|
throw new WorkerError("path_escape", "The requested path escapes the exposed root.");
|
|
50698
51004
|
}
|
|
50699
|
-
const relative =
|
|
51005
|
+
const relative = path7.relative(this.root, candidate);
|
|
50700
51006
|
if (!relative) return;
|
|
50701
51007
|
let current = this.root;
|
|
50702
|
-
for (const segment of relative.split(
|
|
50703
|
-
current =
|
|
51008
|
+
for (const segment of relative.split(path7.sep)) {
|
|
51009
|
+
current = path7.join(current, segment);
|
|
50704
51010
|
try {
|
|
50705
51011
|
const currentStat = await lstat(current);
|
|
50706
51012
|
if (currentStat.isSymbolicLink()) {
|
|
@@ -50723,12 +51029,12 @@ function sha256(content) {
|
|
|
50723
51029
|
return createHash("sha256").update(content).digest("hex");
|
|
50724
51030
|
}
|
|
50725
51031
|
function isPathWithin(root, candidate) {
|
|
50726
|
-
const relative =
|
|
50727
|
-
return relative === "" || !relative.startsWith(`..${
|
|
51032
|
+
const relative = path8.relative(root, candidate);
|
|
51033
|
+
return relative === "" || !relative.startsWith(`..${path8.sep}`) && relative !== ".." && !path8.isAbsolute(relative);
|
|
50728
51034
|
}
|
|
50729
51035
|
var fileWriteTails = /* @__PURE__ */ new Map();
|
|
50730
51036
|
async function withFileWriteLock(target, operation) {
|
|
50731
|
-
const normalized =
|
|
51037
|
+
const normalized = path8.normalize(target);
|
|
50732
51038
|
const key = process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
50733
51039
|
const predecessor = fileWriteTails.get(key);
|
|
50734
51040
|
let release;
|
|
@@ -50745,7 +51051,7 @@ async function withFileWriteLock(target, operation) {
|
|
|
50745
51051
|
}
|
|
50746
51052
|
}
|
|
50747
51053
|
async function withFileWriteLocks(targets, operation) {
|
|
50748
|
-
const unique = [...new Set(targets.map((target) =>
|
|
51054
|
+
const unique = [...new Set(targets.map((target) => path8.normalize(target)))].sort(
|
|
50749
51055
|
(left, right) => left.localeCompare(right)
|
|
50750
51056
|
);
|
|
50751
51057
|
const run = async (index) => {
|
|
@@ -50757,7 +51063,7 @@ async function withFileWriteLocks(targets, operation) {
|
|
|
50757
51063
|
async function requireRevision(target, expectedSha256) {
|
|
50758
51064
|
let actual = null;
|
|
50759
51065
|
try {
|
|
50760
|
-
actual = sha256(await
|
|
51066
|
+
actual = sha256(await readFile4(target));
|
|
50761
51067
|
} catch (error46) {
|
|
50762
51068
|
if (error46.code !== "ENOENT") throw error46;
|
|
50763
51069
|
}
|
|
@@ -50781,7 +51087,7 @@ function compareNames(left, right) {
|
|
|
50781
51087
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
50782
51088
|
}
|
|
50783
51089
|
function relativeSortKey(root, target) {
|
|
50784
|
-
const relative =
|
|
51090
|
+
const relative = path8.relative(root, target);
|
|
50785
51091
|
if (!relative) return ".";
|
|
50786
51092
|
return process.platform === "win32" ? relative.replaceAll("\\", "/") : relative;
|
|
50787
51093
|
}
|
|
@@ -51093,7 +51399,7 @@ var FileService = class {
|
|
|
51093
51399
|
}
|
|
51094
51400
|
async #readResolvedText(target, maximumBytes = MAX_TEXT_BYTES) {
|
|
51095
51401
|
const boundedMaximum = Math.min(maximumBytes, MAX_TEXT_BYTES);
|
|
51096
|
-
const targetStat = await
|
|
51402
|
+
const targetStat = await stat3(target);
|
|
51097
51403
|
if (!targetStat.isFile()) {
|
|
51098
51404
|
throw new WorkerError("not_file", "The requested path is not a file.");
|
|
51099
51405
|
}
|
|
@@ -51136,7 +51442,7 @@ var FileService = class {
|
|
|
51136
51442
|
this.policy.resolveExisting(startPath),
|
|
51137
51443
|
deadlineAt
|
|
51138
51444
|
);
|
|
51139
|
-
const startStat = await this.#withinDeadline(
|
|
51445
|
+
const startStat = await this.#withinDeadline(stat3(start), deadlineAt);
|
|
51140
51446
|
if (!startStat.isDirectory()) {
|
|
51141
51447
|
throw new WorkerError("not_directory", "The requested path is not a directory.");
|
|
51142
51448
|
}
|
|
@@ -51165,7 +51471,7 @@ var FileService = class {
|
|
|
51165
51471
|
if (batch.overflow) throw scanLimitError();
|
|
51166
51472
|
scannedEntries += batch.children.length;
|
|
51167
51473
|
for (const child of batch.children) {
|
|
51168
|
-
const target =
|
|
51474
|
+
const target = path8.join(directory, child.name);
|
|
51169
51475
|
heapPush(frontier, {
|
|
51170
51476
|
target,
|
|
51171
51477
|
sortKey: relativeSortKey(this.policy.root, target),
|
|
@@ -51189,7 +51495,7 @@ var FileService = class {
|
|
|
51189
51495
|
throw scanLimitError();
|
|
51190
51496
|
}
|
|
51191
51497
|
scannedEntries += 1;
|
|
51192
|
-
const target =
|
|
51498
|
+
const target = path8.join(directory, child.name);
|
|
51193
51499
|
let childStat;
|
|
51194
51500
|
try {
|
|
51195
51501
|
childStat = await this.#withinDeadline(
|
|
@@ -51341,7 +51647,7 @@ var FileService = class {
|
|
|
51341
51647
|
if (!patterns) return false;
|
|
51342
51648
|
const normalized = relativePath.replaceAll("\\", "/");
|
|
51343
51649
|
try {
|
|
51344
|
-
return patterns.some((pattern) =>
|
|
51650
|
+
return patterns.some((pattern) => path8.posix.matchesGlob(normalized, pattern));
|
|
51345
51651
|
} catch {
|
|
51346
51652
|
throw new WorkerError("invalid_search", "Search glob pattern is invalid.");
|
|
51347
51653
|
}
|
|
@@ -51359,7 +51665,7 @@ var FileService = class {
|
|
|
51359
51665
|
this.policy.resolveExisting(options.path ?? "."),
|
|
51360
51666
|
deadlineAt
|
|
51361
51667
|
);
|
|
51362
|
-
const startStat = await this.#withinDeadline(
|
|
51668
|
+
const startStat = await this.#withinDeadline(stat3(start), deadlineAt);
|
|
51363
51669
|
const matches = [];
|
|
51364
51670
|
let scannedEntries = 0;
|
|
51365
51671
|
let scannedFiles = 0;
|
|
@@ -51370,7 +51676,7 @@ var FileService = class {
|
|
|
51370
51676
|
const searchFile = async (target) => {
|
|
51371
51677
|
const relative = displayPath(this.policy.root, target);
|
|
51372
51678
|
if (extensions && !extensions.some(
|
|
51373
|
-
(extension) =>
|
|
51679
|
+
(extension) => path8.basename(target).toLowerCase().endsWith(extension)
|
|
51374
51680
|
)) {
|
|
51375
51681
|
return false;
|
|
51376
51682
|
}
|
|
@@ -51465,7 +51771,7 @@ var FileService = class {
|
|
|
51465
51771
|
scannedEntries += batch.children.length;
|
|
51466
51772
|
for (const child of batch.children) {
|
|
51467
51773
|
this.#assertBeforeDeadline(deadlineAt);
|
|
51468
|
-
const target =
|
|
51774
|
+
const target = path8.join(directory, child.name);
|
|
51469
51775
|
let targetType;
|
|
51470
51776
|
if (this.#trustDirectoryEntryTypes) {
|
|
51471
51777
|
if (child.isSymbolicLink()) {
|
|
@@ -51613,12 +51919,12 @@ var FileService = class {
|
|
|
51613
51919
|
);
|
|
51614
51920
|
if (current.exists) return { created: false };
|
|
51615
51921
|
try {
|
|
51616
|
-
await
|
|
51922
|
+
await mkdir5(current.target, { recursive });
|
|
51617
51923
|
} catch (error46) {
|
|
51618
51924
|
if (error46.code !== "EEXIST") throw error46;
|
|
51619
51925
|
}
|
|
51620
51926
|
const resolved = await this.policy.resolveExisting(relativePath);
|
|
51621
|
-
if (!(await
|
|
51927
|
+
if (!(await stat3(resolved)).isDirectory()) {
|
|
51622
51928
|
throw new WorkerError("not_directory", "The destination is not a directory.");
|
|
51623
51929
|
}
|
|
51624
51930
|
return { created: true };
|
|
@@ -51650,12 +51956,12 @@ var FileService = class {
|
|
|
51650
51956
|
try {
|
|
51651
51957
|
if (targetStat.isDirectory()) {
|
|
51652
51958
|
if (recursive) {
|
|
51653
|
-
await
|
|
51959
|
+
await rm4(target, { recursive: true, force: false });
|
|
51654
51960
|
} else {
|
|
51655
51961
|
await rmdir(target);
|
|
51656
51962
|
}
|
|
51657
51963
|
} else {
|
|
51658
|
-
await
|
|
51964
|
+
await rm4(target, { force: false });
|
|
51659
51965
|
}
|
|
51660
51966
|
} catch (error46) {
|
|
51661
51967
|
const code = error46.code;
|
|
@@ -51763,7 +52069,7 @@ var FileService = class {
|
|
|
51763
52069
|
return await withFileWriteLock(target, async () => {
|
|
51764
52070
|
let existingMode;
|
|
51765
52071
|
try {
|
|
51766
|
-
const existingStat = await
|
|
52072
|
+
const existingStat = await stat3(target);
|
|
51767
52073
|
if (existingStat.isFile()) existingMode = existingStat.mode & 4095;
|
|
51768
52074
|
} catch (error46) {
|
|
51769
52075
|
if (error46.code !== "ENOENT") throw error46;
|
|
@@ -51775,9 +52081,9 @@ var FileService = class {
|
|
|
51775
52081
|
);
|
|
51776
52082
|
}
|
|
51777
52083
|
if (expectedSha256) await requireRevision(target, expectedSha256);
|
|
51778
|
-
const temporary =
|
|
52084
|
+
const temporary = path8.join(path8.dirname(target), `.glossa-${randomUUID4()}.tmp`);
|
|
51779
52085
|
try {
|
|
51780
|
-
await
|
|
52086
|
+
await writeFile5(temporary, bytes, { flag: "wx", mode: 384 });
|
|
51781
52087
|
target = await this.policy.resolveWritableFile(relativePath);
|
|
51782
52088
|
const tempStat = await lstat2(temporary);
|
|
51783
52089
|
if (!tempStat.isFile() || tempStat.isSymbolicLink()) {
|
|
@@ -51803,7 +52109,7 @@ var FileService = class {
|
|
|
51803
52109
|
}
|
|
51804
52110
|
}
|
|
51805
52111
|
} finally {
|
|
51806
|
-
await
|
|
52112
|
+
await rm4(temporary, { force: true });
|
|
51807
52113
|
}
|
|
51808
52114
|
return { sha256: sha256(bytes), bytes: bytes.byteLength };
|
|
51809
52115
|
});
|
|
@@ -51830,7 +52136,9 @@ function jobInputContainsRestrictedData(job) {
|
|
|
51830
52136
|
return containsRestrictedAuthenticationData({
|
|
51831
52137
|
query: job.query,
|
|
51832
52138
|
path: job.path,
|
|
51833
|
-
extensions: job.extensions
|
|
52139
|
+
extensions: job.extensions,
|
|
52140
|
+
includeGlobs: job.includeGlobs,
|
|
52141
|
+
excludeGlobs: job.excludeGlobs
|
|
51834
52142
|
});
|
|
51835
52143
|
case "read_file_range":
|
|
51836
52144
|
return containsRestrictedAuthenticationData(job.path);
|
|
@@ -52013,7 +52321,7 @@ var LocalWorker = class _LocalWorker {
|
|
|
52013
52321
|
};
|
|
52014
52322
|
|
|
52015
52323
|
// src/worker/remote-worker.ts
|
|
52016
|
-
import { randomUUID as
|
|
52324
|
+
import { randomUUID as randomUUID5 } from "node:crypto";
|
|
52017
52325
|
var WORKER_REQUEST_TIMEOUT_MS = 19e3;
|
|
52018
52326
|
var DEFAULT_RECONNECT_BASE_MS = 500;
|
|
52019
52327
|
var DEFAULT_RECONNECT_MAX_MS = 1e4;
|
|
@@ -52133,7 +52441,7 @@ var RemoteWorker = class {
|
|
|
52133
52441
|
#reconnectBaseMs;
|
|
52134
52442
|
#reconnectMaxMs;
|
|
52135
52443
|
#heartbeatMs;
|
|
52136
|
-
#workerId =
|
|
52444
|
+
#workerId = randomUUID5();
|
|
52137
52445
|
#onStatus;
|
|
52138
52446
|
constructor(options) {
|
|
52139
52447
|
this.#origin = new URL(options.origin);
|
|
@@ -52177,7 +52485,7 @@ var RemoteWorker = class {
|
|
|
52177
52485
|
if (error46 instanceof DeviceRejectedError || error46 instanceof RelayAccessProfileUnsupportedError) {
|
|
52178
52486
|
throw error46;
|
|
52179
52487
|
}
|
|
52180
|
-
const
|
|
52488
|
+
const delay6 = reconnectDelayMs(
|
|
52181
52489
|
failures,
|
|
52182
52490
|
this.#random,
|
|
52183
52491
|
this.#reconnectBaseMs,
|
|
@@ -52187,10 +52495,10 @@ var RemoteWorker = class {
|
|
|
52187
52495
|
this.#onStatus({
|
|
52188
52496
|
state: "retrying",
|
|
52189
52497
|
error: error46 instanceof Error ? error46 : new Error(String(error46)),
|
|
52190
|
-
retryInMs:
|
|
52498
|
+
retryInMs: delay6
|
|
52191
52499
|
});
|
|
52192
52500
|
try {
|
|
52193
|
-
await this.#sleep(
|
|
52501
|
+
await this.#sleep(delay6, this.#signal);
|
|
52194
52502
|
} catch (sleepError) {
|
|
52195
52503
|
if (this.#signal.aborted) return;
|
|
52196
52504
|
throw sleepError;
|
|
@@ -52269,7 +52577,7 @@ var RemoteWorker = class {
|
|
|
52269
52577
|
const key = JSON.stringify(body);
|
|
52270
52578
|
if (seenAttempts.has(key)) return;
|
|
52271
52579
|
seenAttempts.add(key);
|
|
52272
|
-
attempts.push({ body, legacyRelay
|
|
52580
|
+
attempts.push({ body, legacyRelay });
|
|
52273
52581
|
};
|
|
52274
52582
|
if (this.#accessProfile) {
|
|
52275
52583
|
for (const metadata of metadataPhases) {
|
|
@@ -52294,7 +52602,7 @@ var RemoteWorker = class {
|
|
|
52294
52602
|
);
|
|
52295
52603
|
}
|
|
52296
52604
|
}
|
|
52297
|
-
attempts.push({ body: {}, legacyRelay: true
|
|
52605
|
+
attempts.push({ body: {}, legacyRelay: true });
|
|
52298
52606
|
}
|
|
52299
52607
|
for (const attempt of attempts) {
|
|
52300
52608
|
let response;
|
|
@@ -52314,17 +52622,22 @@ var RemoteWorker = class {
|
|
|
52314
52622
|
throw new Error("The relay returned an invalid registration response.");
|
|
52315
52623
|
}
|
|
52316
52624
|
const workerToken = "workerToken" in value ? optionalWorkerToken(value.workerToken) : void 0;
|
|
52317
|
-
|
|
52625
|
+
const accessProfileAccepted = this.#accessProfile === void 0 || this.#accessProfile === "system" || "accessProfile" in value && value.accessProfile === this.#accessProfile;
|
|
52626
|
+
const session = {
|
|
52318
52627
|
generation: value.generation,
|
|
52319
52628
|
legacyRelay: attempt.legacyRelay,
|
|
52320
52629
|
concurrentJobs: !attempt.legacyRelay && supportsCapability(value, "concurrentJobs"),
|
|
52321
52630
|
structuredReads: !attempt.legacyRelay && supportsCapability(value, "structuredReads"),
|
|
52322
52631
|
structuredMutations: !attempt.legacyRelay && supportsCapability(value, "structuredMutations"),
|
|
52323
52632
|
commandOutputRanges: !attempt.legacyRelay && supportsCapability(value, "commandOutputRanges"),
|
|
52324
|
-
accessProfileAccepted
|
|
52633
|
+
accessProfileAccepted,
|
|
52325
52634
|
workspaceLabelAccepted: this.#workspaceLabel === void 0 || "workspaceLabel" in value && value.workspaceLabel === this.#workspaceLabel,
|
|
52326
52635
|
...workerToken ? { workerToken } : {}
|
|
52327
52636
|
};
|
|
52637
|
+
if (!accessProfileAccepted && this.#accessProfile) {
|
|
52638
|
+
throw new RelayAccessProfileUnsupportedError(this.#accessProfile);
|
|
52639
|
+
}
|
|
52640
|
+
return session;
|
|
52328
52641
|
}
|
|
52329
52642
|
if (this.#accessProfile && this.#accessProfile !== "system") {
|
|
52330
52643
|
throw new RelayAccessProfileUnsupportedError(this.#accessProfile);
|
|
@@ -52567,10 +52880,10 @@ var RemoteWorker = class {
|
|
|
52567
52880
|
await unregister(`Device ${this.#deviceToken}`);
|
|
52568
52881
|
}
|
|
52569
52882
|
}
|
|
52570
|
-
async #post(
|
|
52883
|
+
async #post(path14, body, workerToken) {
|
|
52571
52884
|
const timeout = AbortSignal.timeout(WORKER_REQUEST_TIMEOUT_MS);
|
|
52572
52885
|
const signal = AbortSignal.any([this.#signal, timeout]);
|
|
52573
|
-
const response = await this.#fetcher(new URL(
|
|
52886
|
+
const response = await this.#fetcher(new URL(path14, this.#origin), {
|
|
52574
52887
|
method: "POST",
|
|
52575
52888
|
headers: {
|
|
52576
52889
|
authorization: workerToken ? `Worker ${workerToken}` : `Device ${this.#deviceToken}`,
|
|
@@ -52691,56 +53004,27 @@ function visibleWorker(worker, options) {
|
|
|
52691
53004
|
}
|
|
52692
53005
|
async function deviceForSession(endpoints, dependencies = {}, signal) {
|
|
52693
53006
|
const loadDevice = dependencies.loadDeviceCredential ?? loadDeviceCredential;
|
|
52694
|
-
const loadLogin = dependencies.loadCredentials ?? loadCredentials;
|
|
52695
|
-
const validate2 = dependencies.validCredentials ?? validCredentials;
|
|
52696
|
-
const subjectFor = dependencies.accessTokenSubject ?? accessTokenSubject;
|
|
52697
53007
|
const removeDevice = dependencies.deleteDeviceCredential ?? deleteDeviceCredential;
|
|
52698
|
-
const enroll = dependencies.enrollDevice ?? enrollDevice;
|
|
52699
53008
|
const saveDevice = dependencies.saveDeviceCredential ?? saveDeviceCredential;
|
|
52700
|
-
const
|
|
52701
|
-
const
|
|
52702
|
-
const
|
|
52703
|
-
const fetchRequest = signal ? async (input, init) => await baseFetch(input, { ...init, signal }) : baseFetch;
|
|
53009
|
+
const pair = dependencies.pairDevice ?? pairDevice;
|
|
53010
|
+
const revoke = dependencies.revokePairedDevice ?? revokePairedDevice;
|
|
53011
|
+
const withPairingLease = dependencies.withDevicePairingLease ?? withDevicePairingLease;
|
|
52704
53012
|
signal?.throwIfAborted();
|
|
52705
53013
|
const stored = await loadDevice();
|
|
52706
|
-
|
|
52707
|
-
|
|
52708
|
-
|
|
52709
|
-
const
|
|
52710
|
-
if (
|
|
52711
|
-
|
|
52712
|
-
|
|
52713
|
-
|
|
52714
|
-
|
|
52715
|
-
|
|
52716
|
-
const
|
|
52717
|
-
|
|
52718
|
-
|
|
52719
|
-
|
|
52720
|
-
await saveDevice(migrated);
|
|
52721
|
-
return migrated;
|
|
52722
|
-
}
|
|
52723
|
-
await removeDevice();
|
|
52724
|
-
}
|
|
52725
|
-
signal?.throwIfAborted();
|
|
52726
|
-
const current = await currentCredentials();
|
|
52727
|
-
const enrolled = await enroll(
|
|
52728
|
-
endpoints,
|
|
52729
|
-
current,
|
|
52730
|
-
name(),
|
|
52731
|
-
fetchRequest
|
|
52732
|
-
);
|
|
52733
|
-
const bound = {
|
|
52734
|
-
...enrolled,
|
|
52735
|
-
accountSubject: subjectFor(current)
|
|
52736
|
-
};
|
|
52737
|
-
await saveDevice(bound);
|
|
52738
|
-
return bound;
|
|
52739
|
-
}
|
|
52740
|
-
async function reenrollRejectedDevice(endpoints, dependencies = {}, signal) {
|
|
52741
|
-
const remove = dependencies.deleteDeviceCredential ?? deleteDeviceCredential;
|
|
52742
|
-
await remove();
|
|
52743
|
-
return await deviceForSession(endpoints, dependencies, signal);
|
|
53014
|
+
if (stored?.relayOrigin === endpoints.relayOrigin) return stored;
|
|
53015
|
+
return await withPairingLease(async () => {
|
|
53016
|
+
signal?.throwIfAborted();
|
|
53017
|
+
const current = await loadDevice();
|
|
53018
|
+
if (current?.relayOrigin === endpoints.relayOrigin) return current;
|
|
53019
|
+
if (current) {
|
|
53020
|
+
await revoke({ relayOrigin: current.relayOrigin }, current);
|
|
53021
|
+
await removeDevice();
|
|
53022
|
+
}
|
|
53023
|
+
signal?.throwIfAborted();
|
|
53024
|
+
const paired = await pair(endpoints, signal);
|
|
53025
|
+
await saveDevice(paired);
|
|
53026
|
+
return paired;
|
|
53027
|
+
}, signal);
|
|
52744
53028
|
}
|
|
52745
53029
|
function retryMessage(retryInMs) {
|
|
52746
53030
|
const seconds = Math.max(1, Math.ceil(retryInMs / 1e3));
|
|
@@ -52852,9 +53136,6 @@ async function connectRemoteWorker(endpoints, device, worker, options, signal, o
|
|
|
52852
53136
|
await connectHintTask;
|
|
52853
53137
|
}
|
|
52854
53138
|
}
|
|
52855
|
-
function shouldRecoverRejectedDevice(error46, recoveredRejectedDevice, connected) {
|
|
52856
|
-
return error46 instanceof DeviceRejectedError && !recoveredRejectedDevice && !connected;
|
|
52857
|
-
}
|
|
52858
53139
|
async function runManagedSession(root, endpoints, options = {}) {
|
|
52859
53140
|
const controller = new AbortController();
|
|
52860
53141
|
const stop = () => controller.abort();
|
|
@@ -52869,9 +53150,9 @@ async function runManagedSession(root, endpoints, options = {}) {
|
|
|
52869
53150
|
try {
|
|
52870
53151
|
const accessProfile = options.accessProfile ?? DEFAULT_WORKER_ACCESS_PROFILE;
|
|
52871
53152
|
const sessionOptions = { ...options, accessProfile };
|
|
52872
|
-
|
|
53153
|
+
const device = options.device ?? await deviceForSession(
|
|
52873
53154
|
endpoints,
|
|
52874
|
-
|
|
53155
|
+
{},
|
|
52875
53156
|
controller.signal
|
|
52876
53157
|
);
|
|
52877
53158
|
controller.signal.throwIfAborted();
|
|
@@ -52892,41 +53173,18 @@ async function runManagedSession(root, endpoints, options = {}) {
|
|
|
52892
53173
|
console.error(accessProfileSummary(accessProfile));
|
|
52893
53174
|
console.error("Press Ctrl+C to disconnect.");
|
|
52894
53175
|
}
|
|
52895
|
-
|
|
52896
|
-
|
|
52897
|
-
|
|
52898
|
-
|
|
52899
|
-
|
|
52900
|
-
|
|
52901
|
-
|
|
52902
|
-
|
|
52903
|
-
sessionOptions,
|
|
52904
|
-
controller.signal,
|
|
52905
|
-
() => {
|
|
52906
|
-
connected = true;
|
|
52907
|
-
}
|
|
52908
|
-
);
|
|
52909
|
-
break;
|
|
52910
|
-
} catch (error46) {
|
|
52911
|
-
if (!shouldRecoverRejectedDevice(
|
|
52912
|
-
error46,
|
|
52913
|
-
recoveredRejectedDevice,
|
|
52914
|
-
connected
|
|
52915
|
-
)) {
|
|
52916
|
-
throw error46;
|
|
52917
|
-
}
|
|
52918
|
-
recoveredRejectedDevice = true;
|
|
52919
|
-
device = await reenrollRejectedDevice(
|
|
52920
|
-
endpoints,
|
|
52921
|
-
options.credentials ? { credentials: options.credentials } : {},
|
|
52922
|
-
controller.signal
|
|
52923
|
-
);
|
|
52924
|
-
}
|
|
52925
|
-
}
|
|
53176
|
+
await connectRemoteWorker(
|
|
53177
|
+
endpoints,
|
|
53178
|
+
device,
|
|
53179
|
+
worker,
|
|
53180
|
+
sessionOptions,
|
|
53181
|
+
controller.signal,
|
|
53182
|
+
() => void 0
|
|
53183
|
+
);
|
|
52926
53184
|
} catch (error46) {
|
|
52927
53185
|
if (error46 instanceof DeviceRejectedError) {
|
|
52928
53186
|
await deleteDeviceCredential();
|
|
52929
|
-
throw new Error("The relay rejected this
|
|
53187
|
+
throw new Error("The relay rejected this paired computer. Run Glossa again to pair it with your account.");
|
|
52930
53188
|
}
|
|
52931
53189
|
throw error46;
|
|
52932
53190
|
} finally {
|
|
@@ -52951,7 +53209,7 @@ function initialHudState(workspace) {
|
|
|
52951
53209
|
message: void 0,
|
|
52952
53210
|
activities: [],
|
|
52953
53211
|
activityPage: 0,
|
|
52954
|
-
view: "
|
|
53212
|
+
view: "workspace",
|
|
52955
53213
|
status: void 0,
|
|
52956
53214
|
deviceSelection: 0,
|
|
52957
53215
|
pendingAccessProfile: void 0,
|
|
@@ -53022,12 +53280,12 @@ function formatByteCount(value) {
|
|
|
53022
53280
|
}
|
|
53023
53281
|
return `${(kibibytes / 1024).toFixed(1)} MiB`;
|
|
53024
53282
|
}
|
|
53025
|
-
function workspacePath(
|
|
53026
|
-
return
|
|
53283
|
+
function workspacePath(path14) {
|
|
53284
|
+
return path14 || ".";
|
|
53027
53285
|
}
|
|
53028
|
-
function pathSummary(
|
|
53286
|
+
function pathSummary(path14, details = []) {
|
|
53029
53287
|
return {
|
|
53030
|
-
target: `path ${quoteActivityInput(
|
|
53288
|
+
target: `path ${quoteActivityInput(path14)}`,
|
|
53031
53289
|
details,
|
|
53032
53290
|
truncation: "middle"
|
|
53033
53291
|
};
|
|
@@ -53226,6 +53484,7 @@ var COLORS = {
|
|
|
53226
53484
|
muted: "#aaa4b5",
|
|
53227
53485
|
purple: "#8054ff",
|
|
53228
53486
|
purpleReadable: "#ad98ff",
|
|
53487
|
+
success: "#65d6a6",
|
|
53229
53488
|
coral: "#ff665f",
|
|
53230
53489
|
line: "#5c556e"
|
|
53231
53490
|
};
|
|
@@ -53233,7 +53492,9 @@ function Text2({ color, ...props }) {
|
|
|
53233
53492
|
return color === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { ...props }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { ...props, color });
|
|
53234
53493
|
}
|
|
53235
53494
|
var ACTIVITY_REFRESH_INTERVAL_MS = 1e4;
|
|
53495
|
+
var ACTIVITY_STATUS_COLUMN_WIDTH = 2;
|
|
53236
53496
|
var ACTIVITY_TOOL_COLUMN_WIDTH = 15;
|
|
53497
|
+
var ACTIVITY_AGE_COLUMN_WIDTH = 10;
|
|
53237
53498
|
var ACTIVITY_PREAMBLE_LINES = 1;
|
|
53238
53499
|
var HudStore = class {
|
|
53239
53500
|
#state;
|
|
@@ -53279,10 +53540,10 @@ function activityAge(updatedAt, working, now) {
|
|
|
53279
53540
|
if (elapsedMs < 864e5) return `${Math.floor(elapsedMs / 36e5)}h ago`;
|
|
53280
53541
|
return `${Math.floor(elapsedMs / 864e5)}d ago`;
|
|
53281
53542
|
}
|
|
53282
|
-
function
|
|
53283
|
-
if (activity.state === "
|
|
53284
|
-
if (activity.state === "
|
|
53285
|
-
return COLORS.
|
|
53543
|
+
function activityStatus(activity) {
|
|
53544
|
+
if (activity.state === "failed") return { symbol: "\xD7", tone: COLORS.coral };
|
|
53545
|
+
if (activity.state === "working") return { symbol: "\u25CB", tone: COLORS.purpleReadable };
|
|
53546
|
+
return { symbol: "\u2713", tone: COLORS.success };
|
|
53286
53547
|
}
|
|
53287
53548
|
function activitySummary(activity) {
|
|
53288
53549
|
return [activity.summary.target, ...activity.summary.details].join(" \xB7 ");
|
|
@@ -53298,31 +53559,28 @@ function adjacentAccessProfile(accessProfile, direction) {
|
|
|
53298
53559
|
const index = ACCESS_PROFILES.indexOf(accessProfile);
|
|
53299
53560
|
return ACCESS_PROFILES[index + direction];
|
|
53300
53561
|
}
|
|
53301
|
-
function
|
|
53302
|
-
|
|
53303
|
-
|
|
53304
|
-
|
|
53305
|
-
|
|
53306
|
-
|
|
53307
|
-
|
|
53308
|
-
|
|
53309
|
-
|
|
53310
|
-
|
|
53311
|
-
|
|
53312
|
-
}
|
|
53313
|
-
function
|
|
53314
|
-
|
|
53315
|
-
|
|
53316
|
-
|
|
53317
|
-
}
|
|
53318
|
-
|
|
53319
|
-
|
|
53320
|
-
|
|
53321
|
-
|
|
53322
|
-
|
|
53323
|
-
{ key: "L", label: "Sign out", tone: COLORS.coral },
|
|
53324
|
-
{ key: "Q", label: "Quit", tone: COLORS.coral }
|
|
53325
|
-
];
|
|
53562
|
+
function accessProfileCapabilities(accessProfile) {
|
|
53563
|
+
if (accessProfile === "read-only") {
|
|
53564
|
+
return { summary: "Read files only", detail: "No changes \xB7 no commands" };
|
|
53565
|
+
}
|
|
53566
|
+
if (accessProfile === "workspace") {
|
|
53567
|
+
return { summary: "Read + write files", detail: "Commands disabled" };
|
|
53568
|
+
}
|
|
53569
|
+
return {
|
|
53570
|
+
summary: "Read + write files + commands",
|
|
53571
|
+
detail: "OS account permissions apply"
|
|
53572
|
+
};
|
|
53573
|
+
}
|
|
53574
|
+
function primaryFooterHints() {
|
|
53575
|
+
return [
|
|
53576
|
+
{ key: "A", label: "Activity" },
|
|
53577
|
+
{ key: "W", label: "Workspace" },
|
|
53578
|
+
{ key: "D", label: "Devices" },
|
|
53579
|
+
{ key: "?", label: "Help" },
|
|
53580
|
+
{ key: "L", label: "Account sign out", tone: COLORS.coral },
|
|
53581
|
+
{ key: "Q", label: "Quit", tone: COLORS.coral }
|
|
53582
|
+
];
|
|
53583
|
+
}
|
|
53326
53584
|
function contextualFooterHints(state) {
|
|
53327
53585
|
if (state.view === "activity") {
|
|
53328
53586
|
return [
|
|
@@ -53370,7 +53628,7 @@ function splitFooterHints(hints, usable) {
|
|
|
53370
53628
|
return rows;
|
|
53371
53629
|
}
|
|
53372
53630
|
function buildFooterRows(state, usable) {
|
|
53373
|
-
const rows = splitFooterHints(
|
|
53631
|
+
const rows = splitFooterHints(primaryFooterHints(), usable).map((left) => ({
|
|
53374
53632
|
left,
|
|
53375
53633
|
right: []
|
|
53376
53634
|
}));
|
|
@@ -53391,7 +53649,10 @@ function promptText(state) {
|
|
|
53391
53649
|
if (state.busy) return { message: "Working\u2026" };
|
|
53392
53650
|
if (!state.prompt) return void 0;
|
|
53393
53651
|
if (state.prompt.type === "logout") {
|
|
53394
|
-
return {
|
|
53652
|
+
return {
|
|
53653
|
+
message: "Sign out the CLI account and disconnect? Computer pairing stays active.",
|
|
53654
|
+
choices: "Y confirm N cancel"
|
|
53655
|
+
};
|
|
53395
53656
|
}
|
|
53396
53657
|
if (state.prompt.type === "access-confirm") {
|
|
53397
53658
|
const detail = state.prompt.accessProfile === "system" ? "Commands will inherit this OS account's permissions." : "This allows guarded file writes.";
|
|
@@ -53462,7 +53723,7 @@ function Line({ usable, color }) {
|
|
|
53462
53723
|
}
|
|
53463
53724
|
function Header({ state, usable, bodyBudget, color }) {
|
|
53464
53725
|
const statusColor = state.connection === "error" ? COLORS.coral : COLORS.purpleReadable;
|
|
53465
|
-
const pageTitle = state.view === "activity" ? "
|
|
53726
|
+
const pageTitle = state.view === "activity" ? "Activity" : state.view === "devices" ? "Devices" : state.view === "workspace" ? "Workspace" : state.view === "help" ? "Help" : void 0;
|
|
53466
53727
|
const activityPage = state.view === "activity" ? activityPageInfo(state, bodyBudget) : void 0;
|
|
53467
53728
|
const activityRange = activityPage && activityPage.maxPage > 0 ? ` (${activityPage.rangeStart}-${activityPage.rangeEnd}/${state.activities.length})` : "";
|
|
53468
53729
|
const deviceWindow = state.view === "devices" ? deviceListWindow(state, bodyBudget, usable) : void 0;
|
|
@@ -53488,46 +53749,118 @@ function SectionTitle({ children, color, tone = COLORS.purpleReadable }) {
|
|
|
53488
53749
|
function Blank() {
|
|
53489
53750
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { height: 1 });
|
|
53490
53751
|
}
|
|
53491
|
-
function
|
|
53752
|
+
function ActivityPreviewHeader({ usable, color }) {
|
|
53753
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { width: usable, children: [
|
|
53754
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { flexGrow: 1, flexShrink: 1, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { color, children: "Activity" }) }),
|
|
53755
|
+
usable >= 24 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { marginLeft: 1, flexShrink: 0, children: [
|
|
53756
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { bold: true, color: color ? COLORS.purpleReadable : void 0, children: "A" }),
|
|
53757
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { color: color ? COLORS.muted : void 0, children: " View all" })
|
|
53758
|
+
] }) : null
|
|
53759
|
+
] });
|
|
53760
|
+
}
|
|
53761
|
+
function AccessSectionTitle({ accessProfile, usable, color }) {
|
|
53762
|
+
const lower = adjacentAccessProfile(accessProfile, -1);
|
|
53763
|
+
const higher = adjacentAccessProfile(accessProfile, 1);
|
|
53764
|
+
const key = lower && higher ? "\u2190/\u2192" : lower ? "\u2190" : "\u2192";
|
|
53765
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { width: usable, children: [
|
|
53766
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { color, children: "Access" }),
|
|
53767
|
+
usable >= 24 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { marginLeft: 3, flexShrink: 0, children: [
|
|
53768
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { bold: true, color: color ? COLORS.purpleReadable : void 0, children: key }),
|
|
53769
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { color: color ? COLORS.muted : void 0, children: " Switch" })
|
|
53770
|
+
] }) : null
|
|
53771
|
+
] });
|
|
53772
|
+
}
|
|
53773
|
+
function activityLayout(usable) {
|
|
53774
|
+
const toolWidth = Math.min(
|
|
53775
|
+
ACTIVITY_TOOL_COLUMN_WIDTH,
|
|
53776
|
+
Math.max(0, usable - ACTIVITY_STATUS_COLUMN_WIDTH)
|
|
53777
|
+
);
|
|
53778
|
+
return {
|
|
53779
|
+
toolWidth,
|
|
53780
|
+
showSummary: usable >= ACTIVITY_STATUS_COLUMN_WIDTH + toolWidth + 5,
|
|
53781
|
+
showAge: usable >= ACTIVITY_STATUS_COLUMN_WIDTH + toolWidth + ACTIVITY_AGE_COLUMN_WIDTH + 10
|
|
53782
|
+
};
|
|
53783
|
+
}
|
|
53784
|
+
function WorkspaceView({ state, usable, bodyBudget, color, now }) {
|
|
53492
53785
|
const displayedAccessProfile = state.pendingAccessProfile ?? state.accessProfile;
|
|
53786
|
+
const showConnectionMessage = Boolean(
|
|
53787
|
+
state.message && (state.connection === "retrying" || state.connection === "error")
|
|
53788
|
+
);
|
|
53789
|
+
let fixedLines = 3;
|
|
53790
|
+
if (displayedAccessProfile) fixedLines += 5;
|
|
53791
|
+
if (state.deviceName) fixedLines += 3;
|
|
53792
|
+
if (showConnectionMessage) fixedLines += 2;
|
|
53793
|
+
const activityCapacity = Math.min(3, Math.max(0, bodyBudget - fixedLines - 2));
|
|
53794
|
+
const activityPreview = activityCapacity > 0 ? state.activities.slice(-activityCapacity) : [];
|
|
53493
53795
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { height: bodyBudget, flexDirection: "column", flexShrink: 0, overflow: "hidden", children: [
|
|
53494
53796
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Blank, {}),
|
|
53495
53797
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { color, children: "Workspace" }),
|
|
53496
53798
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { color: color ? COLORS.ink : void 0, wrap: "truncate-middle", children: state.workspace }),
|
|
53497
53799
|
displayedAccessProfile ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
53498
53800
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Blank, {}),
|
|
53499
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
53500
|
-
|
|
53501
|
-
|
|
53801
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
53802
|
+
AccessSectionTitle,
|
|
53803
|
+
{
|
|
53804
|
+
accessProfile: displayedAccessProfile,
|
|
53805
|
+
usable,
|
|
53806
|
+
color
|
|
53807
|
+
}
|
|
53808
|
+
),
|
|
53809
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { bold: true, color: color ? COLORS.ink : void 0, wrap: "truncate", children: accessProfileLabel(displayedAccessProfile) }),
|
|
53810
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { color: color ? COLORS.muted : void 0, wrap: "truncate", children: accessProfileCapabilities(displayedAccessProfile).summary }),
|
|
53811
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { color: color ? COLORS.muted : void 0, wrap: "truncate", children: accessProfileCapabilities(displayedAccessProfile).detail })
|
|
53502
53812
|
] }) : null,
|
|
53503
53813
|
state.deviceName ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
53504
53814
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Blank, {}),
|
|
53505
53815
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { color, children: "Device" }),
|
|
53506
53816
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { color: color ? COLORS.ink : void 0, wrap: "truncate", children: state.deviceName })
|
|
53507
53817
|
] }) : null,
|
|
53508
|
-
|
|
53818
|
+
activityCapacity > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
53819
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Blank, {}),
|
|
53820
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ActivityPreviewHeader, { usable, color }),
|
|
53821
|
+
state.activities.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { color: color ? COLORS.muted : void 0, children: "No activity yet." }) : activityPreview.map((activity) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
53822
|
+
ActivityRow,
|
|
53823
|
+
{
|
|
53824
|
+
activity,
|
|
53825
|
+
usable,
|
|
53826
|
+
color,
|
|
53827
|
+
now
|
|
53828
|
+
},
|
|
53829
|
+
activity.requestId
|
|
53830
|
+
))
|
|
53831
|
+
] }) : null,
|
|
53832
|
+
showConnectionMessage ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
53509
53833
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Blank, {}),
|
|
53510
53834
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { color: color ? COLORS.coral : void 0, wrap: "truncate", children: state.message })
|
|
53511
53835
|
] }) : null
|
|
53512
53836
|
] });
|
|
53513
53837
|
}
|
|
53514
53838
|
function ActivityRow({ activity, usable, color, now }) {
|
|
53515
|
-
const toolWidth =
|
|
53839
|
+
const { toolWidth, showSummary, showAge } = activityLayout(usable);
|
|
53516
53840
|
const age = activityAge(activity.updatedAt, activity.state === "working", now);
|
|
53517
|
-
const
|
|
53518
|
-
const showSummary = usable >= toolWidth + 5;
|
|
53841
|
+
const status = activityStatus(activity);
|
|
53519
53842
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { width: usable, height: 1, flexShrink: 0, children: [
|
|
53843
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { width: ACTIVITY_STATUS_COLUMN_WIDTH, flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { bold: true, color: color ? status.tone : void 0, children: status.symbol }) }),
|
|
53520
53844
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { width: toolWidth, flexShrink: 0, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
53521
53845
|
Text2,
|
|
53522
53846
|
{
|
|
53523
53847
|
bold: true,
|
|
53524
|
-
color: color ?
|
|
53848
|
+
color: color ? COLORS.ink : void 0,
|
|
53525
53849
|
wrap: "truncate",
|
|
53526
53850
|
children: activity.tool
|
|
53527
53851
|
}
|
|
53528
53852
|
) }),
|
|
53529
53853
|
showSummary ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { marginLeft: 2, flexGrow: 1, flexShrink: 1, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { color: color ? COLORS.muted : void 0, wrap: "truncate-middle", children: activitySummary(activity) }) }) : null,
|
|
53530
|
-
showAge ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
53854
|
+
showAge ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
53855
|
+
Box_default,
|
|
53856
|
+
{
|
|
53857
|
+
width: ACTIVITY_AGE_COLUMN_WIDTH,
|
|
53858
|
+
marginLeft: 2,
|
|
53859
|
+
flexShrink: 0,
|
|
53860
|
+
justifyContent: "flex-end",
|
|
53861
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { color: color ? COLORS.muted : void 0, children: age })
|
|
53862
|
+
}
|
|
53863
|
+
) : null
|
|
53531
53864
|
] });
|
|
53532
53865
|
}
|
|
53533
53866
|
function ActivityView({ state, usable, bodyBudget, color, now }) {
|
|
@@ -53662,12 +53995,12 @@ function HelpView({ bodyBudget, color }) {
|
|
|
53662
53995
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(HelpRow, { keyLabel: "W", label: "Workspace", color }),
|
|
53663
53996
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(HelpRow, { keyLabel: "D", label: "Devices", color }),
|
|
53664
53997
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(HelpRow, { keyLabel: "?", label: "Help", color }),
|
|
53665
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(HelpRow, { keyLabel: "Esc", label: "
|
|
53998
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(HelpRow, { keyLabel: "Esc", label: "Workspace", color }),
|
|
53666
53999
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Blank, {}),
|
|
53667
54000
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { color, tone: COLORS.coral, children: "Manage" }),
|
|
53668
54001
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(HelpRow, { keyLabel: "\u2190/\u2192", label: "Change workspace access", color, tone: COLORS.coral }),
|
|
53669
54002
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(HelpRow, { keyLabel: "Enter/R", label: "Revoke selected device", color, tone: COLORS.coral }),
|
|
53670
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(HelpRow, { keyLabel: "L", label: "
|
|
54003
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(HelpRow, { keyLabel: "L", label: "Account sign out", color, tone: COLORS.coral }),
|
|
53671
54004
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Blank, {}),
|
|
53672
54005
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { color, children: "App" }),
|
|
53673
54006
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(HelpRow, { keyLabel: "Q", label: "Disconnect and quit", color, tone: COLORS.coral }),
|
|
@@ -53741,7 +54074,16 @@ function HudScreen({ state, columns, rows, color = true, now = Date.now() }) {
|
|
|
53741
54074
|
bodyBudget: metrics.bodyBudget,
|
|
53742
54075
|
color
|
|
53743
54076
|
}
|
|
53744
|
-
) : state.view === "workspace" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
54077
|
+
) : state.view === "workspace" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
54078
|
+
WorkspaceView,
|
|
54079
|
+
{
|
|
54080
|
+
state,
|
|
54081
|
+
usable: metrics.usable,
|
|
54082
|
+
bodyBudget: metrics.bodyBudget,
|
|
54083
|
+
color,
|
|
54084
|
+
now
|
|
54085
|
+
}
|
|
54086
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(HelpView, { bodyBudget: metrics.bodyBudget, color }),
|
|
53745
54087
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Overlay, { state, usable: metrics.usable, color }),
|
|
53746
54088
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Footer, { rows: metrics.footerRows, usable: metrics.usable, color })
|
|
53747
54089
|
] });
|
|
@@ -53802,7 +54144,7 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
53802
54144
|
const { columns, rows } = use_window_size_default();
|
|
53803
54145
|
const [, setClock] = (0, import_react34.useState)(0);
|
|
53804
54146
|
(0, import_react34.useEffect)(() => {
|
|
53805
|
-
if (state.view !== "activity" || state.activities.length === 0) return;
|
|
54147
|
+
if (state.view !== "activity" && state.view !== "workspace" || state.activities.length === 0) return;
|
|
53806
54148
|
const timer = setInterval(() => setClock((value) => value + 1), ACTIVITY_REFRESH_INTERVAL_MS);
|
|
53807
54149
|
return () => clearInterval(timer);
|
|
53808
54150
|
}, [state.view, state.activities.length]);
|
|
@@ -53914,7 +54256,7 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
53914
54256
|
return;
|
|
53915
54257
|
}
|
|
53916
54258
|
if (key.escape) {
|
|
53917
|
-
store3.update((state2) => ({ ...state2, view: "
|
|
54259
|
+
store3.update((state2) => ({ ...state2, view: "workspace", notice: void 0 }));
|
|
53918
54260
|
return;
|
|
53919
54261
|
}
|
|
53920
54262
|
if (value === "a") {
|
|
@@ -54061,119 +54403,8 @@ async function runSessionHud(actions, input = process.stdin, output = process.st
|
|
|
54061
54403
|
var import_semver = __toESM(require_semver2(), 1);
|
|
54062
54404
|
import { spawn as spawn3 } from "node:child_process";
|
|
54063
54405
|
import { createHash as createHash2 } from "node:crypto";
|
|
54064
|
-
import { chmod as chmod3, readdir as readdir2, rename as rename2, rm as
|
|
54065
|
-
import
|
|
54066
|
-
|
|
54067
|
-
// src/update-lock.ts
|
|
54068
|
-
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
54069
|
-
import { mkdir as mkdir4, readdir, rm as rm3, writeFile as writeFile4 } from "node:fs/promises";
|
|
54070
|
-
import path7 from "node:path";
|
|
54071
|
-
var UPDATE_SUFFIX = ".update";
|
|
54072
|
-
var SESSION_SUFFIX = ".session";
|
|
54073
|
-
function updateRuntimeDirectory() {
|
|
54074
|
-
return path7.join(configDirectory(), "runtime");
|
|
54075
|
-
}
|
|
54076
|
-
function processIsAlive(pid) {
|
|
54077
|
-
if (!Number.isSafeInteger(pid) || pid <= 0) return false;
|
|
54078
|
-
try {
|
|
54079
|
-
process.kill(pid, 0);
|
|
54080
|
-
return true;
|
|
54081
|
-
} catch (error46) {
|
|
54082
|
-
return error46.code === "EPERM";
|
|
54083
|
-
}
|
|
54084
|
-
}
|
|
54085
|
-
function leasePid(name, suffix) {
|
|
54086
|
-
if (!name.endsWith(suffix)) return null;
|
|
54087
|
-
const separator = name.indexOf("-");
|
|
54088
|
-
if (separator <= 0) return null;
|
|
54089
|
-
const pid = Number(name.slice(0, separator));
|
|
54090
|
-
return Number.isSafeInteger(pid) && pid > 0 ? pid : null;
|
|
54091
|
-
}
|
|
54092
|
-
async function activeLeaseFiles(directory, suffix) {
|
|
54093
|
-
let entries;
|
|
54094
|
-
try {
|
|
54095
|
-
entries = await readdir(directory, { withFileTypes: true });
|
|
54096
|
-
} catch (error46) {
|
|
54097
|
-
if (error46.code === "ENOENT") return [];
|
|
54098
|
-
throw error46;
|
|
54099
|
-
}
|
|
54100
|
-
const active = [];
|
|
54101
|
-
for (const entry of entries) {
|
|
54102
|
-
if (!entry.isFile() || !entry.name.endsWith(suffix)) continue;
|
|
54103
|
-
const pid = leasePid(entry.name, suffix);
|
|
54104
|
-
const file2 = path7.join(directory, entry.name);
|
|
54105
|
-
if (pid !== null && processIsAlive(pid)) active.push(file2);
|
|
54106
|
-
else await rm3(file2, { force: true });
|
|
54107
|
-
}
|
|
54108
|
-
return active;
|
|
54109
|
-
}
|
|
54110
|
-
async function updateLockIsActive(directory) {
|
|
54111
|
-
return (await activeLeaseFiles(directory, UPDATE_SUFFIX)).length > 0;
|
|
54112
|
-
}
|
|
54113
|
-
async function activeSessionFiles(directory) {
|
|
54114
|
-
return await activeLeaseFiles(directory, SESSION_SUFFIX);
|
|
54115
|
-
}
|
|
54116
|
-
async function createUpdateLock(directory) {
|
|
54117
|
-
await mkdir4(directory, { recursive: true, mode: 448 });
|
|
54118
|
-
const lockFile = path7.join(
|
|
54119
|
-
directory,
|
|
54120
|
-
`${process.pid}-${randomUUID4()}${UPDATE_SUFFIX}`
|
|
54121
|
-
);
|
|
54122
|
-
const content = `${JSON.stringify({ pid: process.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString() })}
|
|
54123
|
-
`;
|
|
54124
|
-
await writeFile4(lockFile, content, {
|
|
54125
|
-
encoding: "utf8",
|
|
54126
|
-
flag: "wx",
|
|
54127
|
-
mode: 384
|
|
54128
|
-
});
|
|
54129
|
-
try {
|
|
54130
|
-
const contenders = await activeLeaseFiles(directory, UPDATE_SUFFIX);
|
|
54131
|
-
if (contenders.some((file2) => file2 !== lockFile)) {
|
|
54132
|
-
throw new Error("Another Glossa update is already running.");
|
|
54133
|
-
}
|
|
54134
|
-
return lockFile;
|
|
54135
|
-
} catch (error46) {
|
|
54136
|
-
await rm3(lockFile, { force: true });
|
|
54137
|
-
throw error46;
|
|
54138
|
-
}
|
|
54139
|
-
}
|
|
54140
|
-
async function withUpdateLease(action, directory = updateRuntimeDirectory()) {
|
|
54141
|
-
const lockFile = await createUpdateLock(directory);
|
|
54142
|
-
try {
|
|
54143
|
-
if ((await activeSessionFiles(directory)).length > 0) {
|
|
54144
|
-
throw new Error("Disconnect every running Glossa workspace before updating.");
|
|
54145
|
-
}
|
|
54146
|
-
return await action();
|
|
54147
|
-
} finally {
|
|
54148
|
-
await rm3(lockFile, { force: true });
|
|
54149
|
-
}
|
|
54150
|
-
}
|
|
54151
|
-
async function withWorkspaceLease(action, directory = updateRuntimeDirectory()) {
|
|
54152
|
-
await mkdir4(directory, { recursive: true, mode: 448 });
|
|
54153
|
-
if (await updateLockIsActive(directory)) {
|
|
54154
|
-
throw new Error("Glossa is updating. Run this workspace again after the update finishes.");
|
|
54155
|
-
}
|
|
54156
|
-
const leaseFile = path7.join(
|
|
54157
|
-
directory,
|
|
54158
|
-
`${process.pid}-${randomUUID4()}${SESSION_SUFFIX}`
|
|
54159
|
-
);
|
|
54160
|
-
await writeFile4(
|
|
54161
|
-
leaseFile,
|
|
54162
|
-
`${JSON.stringify({ pid: process.pid, startedAt: (/* @__PURE__ */ new Date()).toISOString() })}
|
|
54163
|
-
`,
|
|
54164
|
-
{ encoding: "utf8", flag: "wx", mode: 384 }
|
|
54165
|
-
);
|
|
54166
|
-
try {
|
|
54167
|
-
if (await updateLockIsActive(directory)) {
|
|
54168
|
-
throw new Error("Glossa is updating. Run this workspace again after the update finishes.");
|
|
54169
|
-
}
|
|
54170
|
-
return await action();
|
|
54171
|
-
} finally {
|
|
54172
|
-
await rm3(leaseFile, { force: true });
|
|
54173
|
-
}
|
|
54174
|
-
}
|
|
54175
|
-
|
|
54176
|
-
// src/update-service.ts
|
|
54406
|
+
import { chmod as chmod3, readdir as readdir2, rename as rename2, rm as rm5, writeFile as writeFile6 } from "node:fs/promises";
|
|
54407
|
+
import path9 from "node:path";
|
|
54177
54408
|
var PACKAGE_NAME = "@ariobarin/glossa";
|
|
54178
54409
|
var DEFAULT_REGISTRY_URL = "https://registry.npmjs.org/@ariobarin%2Fglossa";
|
|
54179
54410
|
var DEFAULT_RELEASE_BASE_URL = "https://github.com/ariobarin/glossa/releases/download";
|
|
@@ -54250,8 +54481,8 @@ async function cleanupUpdateBackups(distribution, options = {}) {
|
|
|
54250
54481
|
const platform2 = options.platform ?? process.platform;
|
|
54251
54482
|
if (distribution !== "standalone" || platform2 !== "win32") return;
|
|
54252
54483
|
const executable = options.executablePath ?? process.execPath;
|
|
54253
|
-
const directory =
|
|
54254
|
-
const prefix = `${
|
|
54484
|
+
const directory = path9.dirname(executable);
|
|
54485
|
+
const prefix = `${path9.basename(executable)}.old-`;
|
|
54255
54486
|
let entries;
|
|
54256
54487
|
try {
|
|
54257
54488
|
entries = await readdir2(directory, { withFileTypes: true });
|
|
@@ -54264,7 +54495,7 @@ async function cleanupUpdateBackups(distribution, options = {}) {
|
|
|
54264
54495
|
const ownerPid = backupOwnerPid(entry.name, prefix);
|
|
54265
54496
|
return ownerPid !== null && !processIsAlive(ownerPid);
|
|
54266
54497
|
}).map(async (entry) => {
|
|
54267
|
-
await
|
|
54498
|
+
await rm5(path9.join(directory, entry.name), { force: true }).catch(() => void 0);
|
|
54268
54499
|
})
|
|
54269
54500
|
);
|
|
54270
54501
|
}
|
|
@@ -54340,12 +54571,12 @@ async function installStandaloneUpdate(info, options) {
|
|
|
54340
54571
|
if (actual !== expected) {
|
|
54341
54572
|
throw new Error("Glossa refused to update because the SHA-256 checksum did not match.");
|
|
54342
54573
|
}
|
|
54343
|
-
const download =
|
|
54344
|
-
|
|
54345
|
-
`${
|
|
54574
|
+
const download = path9.join(
|
|
54575
|
+
path9.dirname(destination),
|
|
54576
|
+
`${path9.basename(destination)}.download-${process.pid}`
|
|
54346
54577
|
);
|
|
54347
54578
|
try {
|
|
54348
|
-
await
|
|
54579
|
+
await writeFile6(download, binary, { mode: 493 });
|
|
54349
54580
|
if (platform2 === "win32") {
|
|
54350
54581
|
await replaceWindowsExecutable(download, destination);
|
|
54351
54582
|
} else {
|
|
@@ -54354,7 +54585,7 @@ async function installStandaloneUpdate(info, options) {
|
|
|
54354
54585
|
}
|
|
54355
54586
|
return "installed";
|
|
54356
54587
|
} catch (error46) {
|
|
54357
|
-
await
|
|
54588
|
+
await rm5(download, { force: true });
|
|
54358
54589
|
throw error46;
|
|
54359
54590
|
}
|
|
54360
54591
|
}
|
|
@@ -54368,14 +54599,14 @@ async function installUpdate(info, distribution, options = {}) {
|
|
|
54368
54599
|
}
|
|
54369
54600
|
|
|
54370
54601
|
// src/update-state.ts
|
|
54371
|
-
import { chmod as chmod4, mkdir as
|
|
54372
|
-
import
|
|
54602
|
+
import { chmod as chmod4, mkdir as mkdir6, readFile as readFile5, rename as rename3, rm as rm6, writeFile as writeFile7 } from "node:fs/promises";
|
|
54603
|
+
import path10 from "node:path";
|
|
54373
54604
|
var UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
54374
54605
|
function defaultUpdateChannel(version2) {
|
|
54375
54606
|
return version2.includes("-") ? "beta" : "stable";
|
|
54376
54607
|
}
|
|
54377
54608
|
function updateStateFile() {
|
|
54378
|
-
return
|
|
54609
|
+
return path10.join(configDirectory(), "updates.json");
|
|
54379
54610
|
}
|
|
54380
54611
|
function isPolicy(value) {
|
|
54381
54612
|
return value === "notify" || value === "auto" || value === "off";
|
|
@@ -54392,7 +54623,7 @@ async function loadUpdateState(currentVersion, file2 = updateStateFile()) {
|
|
|
54392
54623
|
channel: defaultUpdateChannel(currentVersion)
|
|
54393
54624
|
};
|
|
54394
54625
|
try {
|
|
54395
|
-
const parsed = JSON.parse(await
|
|
54626
|
+
const parsed = JSON.parse(await readFile5(file2, "utf8"));
|
|
54396
54627
|
const lastCheckedAt = optionalString(parsed.lastCheckedAt);
|
|
54397
54628
|
const latestVersion = optionalString(parsed.latestVersion);
|
|
54398
54629
|
return {
|
|
@@ -54409,11 +54640,11 @@ async function loadUpdateState(currentVersion, file2 = updateStateFile()) {
|
|
|
54409
54640
|
}
|
|
54410
54641
|
}
|
|
54411
54642
|
async function saveUpdateState(state, file2 = updateStateFile()) {
|
|
54412
|
-
const directory =
|
|
54643
|
+
const directory = path10.dirname(file2);
|
|
54413
54644
|
const temporary = `${file2}.${process.pid}.tmp`;
|
|
54414
|
-
await
|
|
54645
|
+
await mkdir6(directory, { recursive: true, mode: 448 });
|
|
54415
54646
|
try {
|
|
54416
|
-
await
|
|
54647
|
+
await writeFile7(temporary, `${JSON.stringify(state, null, 2)}
|
|
54417
54648
|
`, {
|
|
54418
54649
|
encoding: "utf8",
|
|
54419
54650
|
mode: 384
|
|
@@ -54421,7 +54652,7 @@ async function saveUpdateState(state, file2 = updateStateFile()) {
|
|
|
54421
54652
|
if (process.platform !== "win32") await chmod4(temporary, 384);
|
|
54422
54653
|
await rename3(temporary, file2);
|
|
54423
54654
|
} finally {
|
|
54424
|
-
await
|
|
54655
|
+
await rm6(temporary, { force: true });
|
|
54425
54656
|
}
|
|
54426
54657
|
}
|
|
54427
54658
|
async function configureUpdates(currentVersion, changes, file2 = updateStateFile()) {
|
|
@@ -54448,15 +54679,31 @@ function isUpdateCheckDue(lastCheckedAt, now = Date.now()) {
|
|
|
54448
54679
|
return !Number.isFinite(checkedAt) || now - checkedAt >= UPDATE_CHECK_INTERVAL_MS;
|
|
54449
54680
|
}
|
|
54450
54681
|
|
|
54682
|
+
// src/unpair.ts
|
|
54683
|
+
async function unpairComputer(dependencies = {}) {
|
|
54684
|
+
const load = dependencies.loadDeviceCredential ?? loadDeviceCredential;
|
|
54685
|
+
const remove = dependencies.deleteDeviceCredential ?? deleteDeviceCredential;
|
|
54686
|
+
const revoke = dependencies.revokePairedDevice ?? revokePairedDevice;
|
|
54687
|
+
const log = dependencies.log ?? console.log;
|
|
54688
|
+
const device = await load();
|
|
54689
|
+
if (!device) {
|
|
54690
|
+
log("This computer is not paired with Glossa.");
|
|
54691
|
+
return;
|
|
54692
|
+
}
|
|
54693
|
+
await revoke({ relayOrigin: device.relayOrigin }, device);
|
|
54694
|
+
await remove();
|
|
54695
|
+
log("Unpaired this computer from Glossa.");
|
|
54696
|
+
}
|
|
54697
|
+
|
|
54451
54698
|
// src/usage-store.ts
|
|
54452
|
-
import { appendFile, chmod as chmod5, mkdir as
|
|
54453
|
-
import
|
|
54699
|
+
import { appendFile, chmod as chmod5, mkdir as mkdir7, readFile as readFile6 } from "node:fs/promises";
|
|
54700
|
+
import path11 from "node:path";
|
|
54454
54701
|
function usageFile() {
|
|
54455
|
-
return
|
|
54702
|
+
return path11.join(configDirectory(), "usage.jsonl");
|
|
54456
54703
|
}
|
|
54457
54704
|
async function recordUsageEvent(event, file2 = usageFile()) {
|
|
54458
|
-
const directory =
|
|
54459
|
-
await
|
|
54705
|
+
const directory = path11.dirname(file2);
|
|
54706
|
+
await mkdir7(directory, { recursive: true, mode: 448 });
|
|
54460
54707
|
await appendFile(file2, `${JSON.stringify(event)}
|
|
54461
54708
|
`, {
|
|
54462
54709
|
encoding: "utf8",
|
|
@@ -54474,15 +54721,15 @@ async function recordToolUsage(tool, ok, at = /* @__PURE__ */ new Date(), file2
|
|
|
54474
54721
|
// src/worker/root-selection.ts
|
|
54475
54722
|
import { realpath as realpath2 } from "node:fs/promises";
|
|
54476
54723
|
import os6 from "node:os";
|
|
54477
|
-
import
|
|
54724
|
+
import path12 from "node:path";
|
|
54478
54725
|
function containsPath(root, candidate) {
|
|
54479
|
-
const relative =
|
|
54480
|
-
return relative !== "" && relative !== ".." && !relative.startsWith(`..${
|
|
54726
|
+
const relative = path12.relative(root, candidate);
|
|
54727
|
+
return relative !== "" && relative !== ".." && !relative.startsWith(`..${path12.sep}`) && !path12.isAbsolute(relative);
|
|
54481
54728
|
}
|
|
54482
54729
|
async function rejectImplicitHomeAncestor(root) {
|
|
54483
54730
|
const homes = await Promise.all(
|
|
54484
54731
|
[os6.homedir(), accountHomeDirectory()].map(
|
|
54485
|
-
async (home) => await realpath2(home).catch(() =>
|
|
54732
|
+
async (home) => await realpath2(home).catch(() => path12.resolve(home))
|
|
54486
54733
|
)
|
|
54487
54734
|
);
|
|
54488
54735
|
if (homes.some((home) => containsPath(root, home))) {
|
|
@@ -54500,10 +54747,10 @@ async function selectExposureRoot(explicitPath, cwd2 = process.cwd()) {
|
|
|
54500
54747
|
|
|
54501
54748
|
// src/worker/workspace-lease.ts
|
|
54502
54749
|
import { createHash as createHash3 } from "node:crypto";
|
|
54503
|
-
import { lstat as lstat3, mkdir as
|
|
54750
|
+
import { lstat as lstat3, mkdir as mkdir8, rm as rm7, stat as stat4 } from "node:fs/promises";
|
|
54504
54751
|
import net from "node:net";
|
|
54505
|
-
import
|
|
54506
|
-
import { setTimeout as
|
|
54752
|
+
import path13 from "node:path";
|
|
54753
|
+
import { setTimeout as delay5 } from "node:timers/promises";
|
|
54507
54754
|
var GUARD_STALE_MS = 5e3;
|
|
54508
54755
|
var GUARD_RETRY_MS = 10;
|
|
54509
54756
|
var GUARD_TIMEOUT_MS = 1e4;
|
|
@@ -54526,12 +54773,12 @@ function workspaceIdentity(root) {
|
|
|
54526
54773
|
}
|
|
54527
54774
|
function defaultLeaseDirectory() {
|
|
54528
54775
|
if (typeof process.getuid === "function") {
|
|
54529
|
-
return
|
|
54776
|
+
return path13.join("/tmp", `glossa-workspaces-${process.getuid()}`);
|
|
54530
54777
|
}
|
|
54531
|
-
return
|
|
54778
|
+
return path13.join(accountHomeDirectory(), ".glossa-workspace-leases");
|
|
54532
54779
|
}
|
|
54533
54780
|
async function ensureLeaseDirectory(directory) {
|
|
54534
|
-
await
|
|
54781
|
+
await mkdir8(directory, { recursive: true, mode: 448 });
|
|
54535
54782
|
const directoryStat = await lstat3(directory);
|
|
54536
54783
|
if (directoryStat.isSymbolicLink() || !directoryStat.isDirectory()) {
|
|
54537
54784
|
throw new Error("The Glossa workspace lease location is not a private directory.");
|
|
@@ -54548,26 +54795,26 @@ function endpointFor(identity, directory) {
|
|
|
54548
54795
|
if (process.platform === "win32") {
|
|
54549
54796
|
return `\\\\.\\pipe\\glossa-workspace-${identity}`;
|
|
54550
54797
|
}
|
|
54551
|
-
return
|
|
54798
|
+
return path13.join(directory, `${identity}.sock`);
|
|
54552
54799
|
}
|
|
54553
|
-
async function
|
|
54554
|
-
const guard =
|
|
54800
|
+
async function acquireGuard2(directory, identity) {
|
|
54801
|
+
const guard = path13.join(directory, `${identity}.guard`);
|
|
54555
54802
|
const deadline = Date.now() + GUARD_TIMEOUT_MS;
|
|
54556
54803
|
while (true) {
|
|
54557
54804
|
try {
|
|
54558
|
-
await
|
|
54559
|
-
return async () => await
|
|
54805
|
+
await mkdir8(guard, { mode: 448 });
|
|
54806
|
+
return async () => await rm7(guard, { recursive: true, force: true });
|
|
54560
54807
|
} catch (error46) {
|
|
54561
54808
|
if (error46.code !== "EEXIST") throw error46;
|
|
54562
54809
|
let stale = false;
|
|
54563
54810
|
try {
|
|
54564
|
-
stale = Date.now() - (await
|
|
54811
|
+
stale = Date.now() - (await stat4(guard)).mtimeMs >= GUARD_STALE_MS;
|
|
54565
54812
|
} catch (statError) {
|
|
54566
54813
|
if (statError.code === "ENOENT") continue;
|
|
54567
54814
|
throw statError;
|
|
54568
54815
|
}
|
|
54569
54816
|
if (stale) {
|
|
54570
|
-
await
|
|
54817
|
+
await rm7(guard, { recursive: true, force: true });
|
|
54571
54818
|
continue;
|
|
54572
54819
|
}
|
|
54573
54820
|
if (Date.now() >= deadline) {
|
|
@@ -54575,7 +54822,7 @@ async function acquireGuard(directory, identity) {
|
|
|
54575
54822
|
"Timed out while checking whether this workspace is already active."
|
|
54576
54823
|
);
|
|
54577
54824
|
}
|
|
54578
|
-
await
|
|
54825
|
+
await delay5(GUARD_RETRY_MS);
|
|
54579
54826
|
}
|
|
54580
54827
|
}
|
|
54581
54828
|
}
|
|
@@ -54623,7 +54870,7 @@ async function acquireWorkspaceLease(root, options = {}) {
|
|
|
54623
54870
|
await ensureLeaseDirectory(userDirectory);
|
|
54624
54871
|
const identity = workspaceIdentity(root);
|
|
54625
54872
|
const endpoint3 = endpointFor(identity, userDirectory);
|
|
54626
|
-
const releaseGuard = await
|
|
54873
|
+
const releaseGuard = await acquireGuard2(userDirectory, identity);
|
|
54627
54874
|
let server;
|
|
54628
54875
|
try {
|
|
54629
54876
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
@@ -54637,11 +54884,11 @@ async function acquireWorkspaceLease(root, options = {}) {
|
|
|
54637
54884
|
if (released) return;
|
|
54638
54885
|
released = true;
|
|
54639
54886
|
await ensureLeaseDirectory(userDirectory);
|
|
54640
|
-
const releaseGuard2 = await
|
|
54887
|
+
const releaseGuard2 = await acquireGuard2(userDirectory, identity);
|
|
54641
54888
|
try {
|
|
54642
54889
|
await closeServer(server);
|
|
54643
54890
|
if (process.platform !== "win32") {
|
|
54644
|
-
await
|
|
54891
|
+
await rm7(endpoint3, { force: true });
|
|
54645
54892
|
}
|
|
54646
54893
|
} finally {
|
|
54647
54894
|
await releaseGuard2();
|
|
@@ -54654,7 +54901,7 @@ async function acquireWorkspaceLease(root, options = {}) {
|
|
|
54654
54901
|
throw new WorkspaceAlreadyActiveError();
|
|
54655
54902
|
}
|
|
54656
54903
|
if (error46.code !== "EADDRINUSE") throw error46;
|
|
54657
|
-
if (process.platform !== "win32") await
|
|
54904
|
+
if (process.platform !== "win32") await rm7(endpoint3, { force: true });
|
|
54658
54905
|
}
|
|
54659
54906
|
}
|
|
54660
54907
|
throw new Error("Could not reserve this workspace for the current Glossa process.");
|
|
@@ -54664,7 +54911,7 @@ async function acquireWorkspaceLease(root, options = {}) {
|
|
|
54664
54911
|
}
|
|
54665
54912
|
|
|
54666
54913
|
// src/main.ts
|
|
54667
|
-
var VERSION = "0.2.0
|
|
54914
|
+
var VERSION = "0.2.0";
|
|
54668
54915
|
var DISTRIBUTION = "npm";
|
|
54669
54916
|
var HELP = `Glossa ${VERSION}
|
|
54670
54917
|
|
|
@@ -54673,6 +54920,7 @@ Usage:
|
|
|
54673
54920
|
glossa status
|
|
54674
54921
|
glossa devices revoke <id>
|
|
54675
54922
|
glossa logout
|
|
54923
|
+
glossa unpair
|
|
54676
54924
|
glossa update [--check]
|
|
54677
54925
|
glossa update --policy <notify|auto|off>
|
|
54678
54926
|
glossa update --channel <beta|stable>
|
|
@@ -54692,8 +54940,8 @@ Keys:
|
|
|
54692
54940
|
\u2191\u2193 select or browse
|
|
54693
54941
|
\u2190\u2192 change workspace access
|
|
54694
54942
|
Enter/r revoke selected device
|
|
54695
|
-
Esc
|
|
54696
|
-
l sign out
|
|
54943
|
+
Esc workspace
|
|
54944
|
+
l account sign out
|
|
54697
54945
|
? help
|
|
54698
54946
|
q disconnect and quit`;
|
|
54699
54947
|
async function withLoginSignal(action) {
|
|
@@ -54740,8 +54988,8 @@ async function revokeKnownDevice(deviceId) {
|
|
|
54740
54988
|
const credentials = await authenticatedCredentials();
|
|
54741
54989
|
await revokeDevice(loadRelayEndpoints(), credentials, deviceId);
|
|
54742
54990
|
}
|
|
54743
|
-
async function runWorkspaceSession(
|
|
54744
|
-
const root = await selectExposureRoot(
|
|
54991
|
+
async function runWorkspaceSession(path14, label, accessProfile) {
|
|
54992
|
+
const root = await selectExposureRoot(path14);
|
|
54745
54993
|
const lease = await acquireWorkspaceLease(root);
|
|
54746
54994
|
let usageWrites = Promise.resolve();
|
|
54747
54995
|
const queueUsage = (write) => {
|
|
@@ -54749,8 +54997,7 @@ async function runWorkspaceSession(path13, label, accessProfile) {
|
|
|
54749
54997
|
};
|
|
54750
54998
|
try {
|
|
54751
54999
|
const endpoints = loadRelayEndpoints();
|
|
54752
|
-
|
|
54753
|
-
const statusService = new WorkspaceStatusService(credentials, endpoints);
|
|
55000
|
+
const device = await deviceForSession(endpoints);
|
|
54754
55001
|
let postExitNotice;
|
|
54755
55002
|
let requestedAccessProfile = accessProfile;
|
|
54756
55003
|
let activeSessionController;
|
|
@@ -54768,7 +55015,7 @@ async function runWorkspaceSession(path13, label, accessProfile) {
|
|
|
54768
55015
|
else signal.addEventListener("abort", stopSession, { once: true });
|
|
54769
55016
|
try {
|
|
54770
55017
|
await runManagedSession(root, endpoints, {
|
|
54771
|
-
|
|
55018
|
+
device,
|
|
54772
55019
|
workerVersion: VERSION,
|
|
54773
55020
|
accessProfile: sessionAccessProfile,
|
|
54774
55021
|
...label ? { workspaceLabel: label } : {},
|
|
@@ -54799,10 +55046,13 @@ async function runWorkspaceSession(path13, label, accessProfile) {
|
|
|
54799
55046
|
}
|
|
54800
55047
|
},
|
|
54801
55048
|
loadStatus: async (signal) => {
|
|
54802
|
-
|
|
55049
|
+
const credentials = await authenticatedCredentials(signal);
|
|
55050
|
+
return hudStatus(
|
|
55051
|
+
await new WorkspaceStatusService(credentials, endpoints).refresh(signal)
|
|
55052
|
+
);
|
|
54803
55053
|
},
|
|
54804
55054
|
revokeDevice: async (deviceId, signal) => {
|
|
54805
|
-
credentials = await
|
|
55055
|
+
const credentials = await authenticatedCredentials(signal);
|
|
54806
55056
|
await revokeDevice(
|
|
54807
55057
|
endpoints,
|
|
54808
55058
|
credentials,
|
|
@@ -54823,9 +55073,9 @@ async function runWorkspaceSession(path13, label, accessProfile) {
|
|
|
54823
55073
|
await lease.release();
|
|
54824
55074
|
}
|
|
54825
55075
|
}
|
|
54826
|
-
async function runWorkspace(
|
|
55076
|
+
async function runWorkspace(path14, label, accessProfile) {
|
|
54827
55077
|
await withWorkspaceLease(
|
|
54828
|
-
async () => await runWorkspaceSession(
|
|
55078
|
+
async () => await runWorkspaceSession(path14, label, accessProfile)
|
|
54829
55079
|
);
|
|
54830
55080
|
}
|
|
54831
55081
|
async function refreshUpdateInfo(timeoutMs) {
|
|
@@ -54928,6 +55178,8 @@ async function main() {
|
|
|
54928
55178
|
await showStatus();
|
|
54929
55179
|
} else if (invocation.command === "logout") {
|
|
54930
55180
|
await logoutFromGlossa();
|
|
55181
|
+
} else if (invocation.command === "unpair") {
|
|
55182
|
+
await unpairComputer();
|
|
54931
55183
|
} else if (invocation.command === "update") {
|
|
54932
55184
|
await runUpdateCommand(invocation);
|
|
54933
55185
|
} else {
|