@ariobarin/glossa 0.2.0 → 0.2.2
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 +2 -5
- package/dist/app.js +512 -1353
- 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, path12, index, value) {
|
|
10440
|
+
if (index >= path12.length) return value;
|
|
10441
|
+
var key = path12[index], updated = isArrayImpl(obj) ? obj.slice() : assign({}, obj);
|
|
10442
|
+
updated[key] = copyWithSetImpl(obj[key], path12, 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, path12, index) {
|
|
10470
|
+
var key = path12[index], updated = isArrayImpl(obj) ? obj.slice() : assign({}, obj);
|
|
10471
|
+
if (index + 1 === path12.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], path12, 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, path12, value) {
|
|
23751
23751
|
id = findHook(fiber, id);
|
|
23752
|
-
null !== id && (
|
|
23752
|
+
null !== id && (path12 = copyWithSetImpl(id.memoizedState, path12, 0, value), id.memoizedState = path12, id.baseState = path12, fiber.memoizedProps = assign({}, fiber.memoizedProps), path12 = enqueueConcurrentRenderForLane(fiber, 2), null !== path12 && scheduleUpdateOnFiber(path12, fiber, 2));
|
|
23753
23753
|
};
|
|
23754
|
-
overrideHookStateDeletePath = function(fiber, id,
|
|
23754
|
+
overrideHookStateDeletePath = function(fiber, id, path12) {
|
|
23755
23755
|
id = findHook(fiber, id);
|
|
23756
|
-
null !== id && (
|
|
23756
|
+
null !== id && (path12 = copyWithDeleteImpl(id.memoizedState, path12, 0), id.memoizedState = path12, id.baseState = path12, fiber.memoizedProps = assign({}, fiber.memoizedProps), path12 = enqueueConcurrentRenderForLane(fiber, 2), null !== path12 && scheduleUpdateOnFiber(path12, 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, path12, value) {
|
|
23763
|
+
fiber.pendingProps = copyWithSetImpl(fiber.memoizedProps, path12, 0, value);
|
|
23764
23764
|
fiber.alternate && (fiber.alternate.pendingProps = fiber.pendingProps);
|
|
23765
|
-
|
|
23766
|
-
null !==
|
|
23765
|
+
path12 = enqueueConcurrentRenderForLane(fiber, 2);
|
|
23766
|
+
null !== path12 && scheduleUpdateOnFiber(path12, fiber, 2);
|
|
23767
23767
|
};
|
|
23768
|
-
overridePropsDeletePath = function(fiber,
|
|
23769
|
-
fiber.pendingProps = copyWithDeleteImpl(fiber.memoizedProps,
|
|
23768
|
+
overridePropsDeletePath = function(fiber, path12) {
|
|
23769
|
+
fiber.pendingProps = copyWithDeleteImpl(fiber.memoizedProps, path12, 0);
|
|
23770
23770
|
fiber.alternate && (fiber.alternate.pendingProps = fiber.pendingProps);
|
|
23771
|
-
|
|
23772
|
-
null !==
|
|
23771
|
+
path12 = enqueueConcurrentRenderForLane(fiber, 2);
|
|
23772
|
+
null !== path12 && scheduleUpdateOnFiber(path12, fiber, 2);
|
|
23773
23773
|
};
|
|
23774
23774
|
overridePropsRenamePath = function(fiber, oldPath, newPath) {
|
|
23775
23775
|
fiber.pendingProps = copyWithRename(
|
|
@@ -26745,529 +26745,6 @@ var require_semver2 = __commonJS({
|
|
|
26745
26745
|
}
|
|
26746
26746
|
});
|
|
26747
26747
|
|
|
26748
|
-
// src/auth-config.ts
|
|
26749
|
-
var DEFAULT_AUTH_CONFIG = {
|
|
26750
|
-
issuer: "https://dev-fl2h5xhp6umeh74m.us.auth0.com/",
|
|
26751
|
-
clientId: "9mwnK9nTAd8q1kxnKIZxC1wodxzfWHg5",
|
|
26752
|
-
audience: "https://mcp.glossa.sh/",
|
|
26753
|
-
scope: "openid profile offline_access glossa:device"
|
|
26754
|
-
};
|
|
26755
|
-
function configuredValue(environment, name, fallback) {
|
|
26756
|
-
const value = environment[name]?.trim();
|
|
26757
|
-
return value || fallback;
|
|
26758
|
-
}
|
|
26759
|
-
function loadAuthConfig(environment = process.env) {
|
|
26760
|
-
return {
|
|
26761
|
-
issuer: configuredValue(environment, "GLOSSA_AUTH0_ISSUER", DEFAULT_AUTH_CONFIG.issuer),
|
|
26762
|
-
clientId: configuredValue(
|
|
26763
|
-
environment,
|
|
26764
|
-
"GLOSSA_AUTH0_CLI_CLIENT_ID",
|
|
26765
|
-
DEFAULT_AUTH_CONFIG.clientId
|
|
26766
|
-
),
|
|
26767
|
-
audience: configuredValue(
|
|
26768
|
-
environment,
|
|
26769
|
-
"GLOSSA_AUTH0_AUDIENCE",
|
|
26770
|
-
DEFAULT_AUTH_CONFIG.audience
|
|
26771
|
-
),
|
|
26772
|
-
scope: DEFAULT_AUTH_CONFIG.scope
|
|
26773
|
-
};
|
|
26774
|
-
}
|
|
26775
|
-
|
|
26776
|
-
// src/config-store.ts
|
|
26777
|
-
import path2 from "node:path";
|
|
26778
|
-
|
|
26779
|
-
// src/secure-store.ts
|
|
26780
|
-
import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
26781
|
-
import os from "node:os";
|
|
26782
|
-
import path from "node:path";
|
|
26783
|
-
var KEYRING_SERVICE = "Glossa";
|
|
26784
|
-
function configDirectory() {
|
|
26785
|
-
if (process.platform === "win32") {
|
|
26786
|
-
return path.join(process.env.APPDATA ?? os.homedir(), "Glossa");
|
|
26787
|
-
}
|
|
26788
|
-
return path.join(
|
|
26789
|
-
process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config"),
|
|
26790
|
-
"glossa"
|
|
26791
|
-
);
|
|
26792
|
-
}
|
|
26793
|
-
var SecureStore = class {
|
|
26794
|
-
#options;
|
|
26795
|
-
#warned = false;
|
|
26796
|
-
constructor(options) {
|
|
26797
|
-
this.#options = options;
|
|
26798
|
-
}
|
|
26799
|
-
async save(value) {
|
|
26800
|
-
const serialized = JSON.stringify(value);
|
|
26801
|
-
const entry = await this.#entry();
|
|
26802
|
-
if (entry) {
|
|
26803
|
-
try {
|
|
26804
|
-
await this.#writeEntry(entry, serialized);
|
|
26805
|
-
await rm(this.#options.file, { force: true });
|
|
26806
|
-
return "keyring";
|
|
26807
|
-
} catch {
|
|
26808
|
-
}
|
|
26809
|
-
}
|
|
26810
|
-
this.#warn();
|
|
26811
|
-
await mkdir(path.dirname(this.#options.file), {
|
|
26812
|
-
recursive: true,
|
|
26813
|
-
mode: 448
|
|
26814
|
-
});
|
|
26815
|
-
await writeFile(
|
|
26816
|
-
this.#options.file,
|
|
26817
|
-
`${JSON.stringify(value, null, 2)}
|
|
26818
|
-
`,
|
|
26819
|
-
{ encoding: "utf8", mode: 384 }
|
|
26820
|
-
);
|
|
26821
|
-
if (process.platform !== "win32") {
|
|
26822
|
-
await chmod(this.#options.file, 384);
|
|
26823
|
-
}
|
|
26824
|
-
return "file";
|
|
26825
|
-
}
|
|
26826
|
-
async load() {
|
|
26827
|
-
const entry = await this.#entry();
|
|
26828
|
-
if (entry) {
|
|
26829
|
-
const value2 = await this.#readEntry(entry, true);
|
|
26830
|
-
if (value2 != null) return { value: value2, backend: "keyring" };
|
|
26831
|
-
}
|
|
26832
|
-
let value;
|
|
26833
|
-
try {
|
|
26834
|
-
value = this.#options.parse(await readFile(this.#options.file, "utf8"));
|
|
26835
|
-
} catch (error46) {
|
|
26836
|
-
if (error46.code === "ENOENT") return null;
|
|
26837
|
-
throw error46;
|
|
26838
|
-
}
|
|
26839
|
-
if (entry) {
|
|
26840
|
-
try {
|
|
26841
|
-
await this.#writeEntry(entry, JSON.stringify(value));
|
|
26842
|
-
await rm(this.#options.file, { force: true });
|
|
26843
|
-
return { value, backend: "keyring" };
|
|
26844
|
-
} catch {
|
|
26845
|
-
}
|
|
26846
|
-
}
|
|
26847
|
-
this.#warn();
|
|
26848
|
-
return { value, backend: "file" };
|
|
26849
|
-
}
|
|
26850
|
-
async peek() {
|
|
26851
|
-
const entry = await this.#entry();
|
|
26852
|
-
if (entry) {
|
|
26853
|
-
const value = await this.#readEntry(entry, false);
|
|
26854
|
-
if (value != null) return { value, backend: "keyring" };
|
|
26855
|
-
}
|
|
26856
|
-
try {
|
|
26857
|
-
return {
|
|
26858
|
-
value: this.#options.parse(await readFile(this.#options.file, "utf8")),
|
|
26859
|
-
backend: "file"
|
|
26860
|
-
};
|
|
26861
|
-
} catch (error46) {
|
|
26862
|
-
if (error46.code === "ENOENT") return null;
|
|
26863
|
-
throw error46;
|
|
26864
|
-
}
|
|
26865
|
-
}
|
|
26866
|
-
async delete() {
|
|
26867
|
-
const entry = await this.#entry();
|
|
26868
|
-
let keyringDeleteFailed = false;
|
|
26869
|
-
if (entry) {
|
|
26870
|
-
try {
|
|
26871
|
-
const deleted = await entry.deleteCredential();
|
|
26872
|
-
if (!deleted && await entry.getSecret() != null) {
|
|
26873
|
-
keyringDeleteFailed = true;
|
|
26874
|
-
}
|
|
26875
|
-
} catch {
|
|
26876
|
-
keyringDeleteFailed = true;
|
|
26877
|
-
}
|
|
26878
|
-
}
|
|
26879
|
-
await rm(this.#options.file, { force: true });
|
|
26880
|
-
if (keyringDeleteFailed) {
|
|
26881
|
-
throw new Error(
|
|
26882
|
-
"The operating-system credential store could not remove the Glossa credential."
|
|
26883
|
-
);
|
|
26884
|
-
}
|
|
26885
|
-
}
|
|
26886
|
-
async #entry() {
|
|
26887
|
-
if (this.#options.entryProvider) {
|
|
26888
|
-
try {
|
|
26889
|
-
return await this.#options.entryProvider();
|
|
26890
|
-
} catch {
|
|
26891
|
-
return null;
|
|
26892
|
-
}
|
|
26893
|
-
}
|
|
26894
|
-
try {
|
|
26895
|
-
const { AsyncEntry } = await import("@napi-rs/keyring");
|
|
26896
|
-
return new AsyncEntry(KEYRING_SERVICE, this.#options.account);
|
|
26897
|
-
} catch {
|
|
26898
|
-
return null;
|
|
26899
|
-
}
|
|
26900
|
-
}
|
|
26901
|
-
async #readEntry(entry, migrateLegacy) {
|
|
26902
|
-
let found = false;
|
|
26903
|
-
try {
|
|
26904
|
-
const secret = await entry.getSecret();
|
|
26905
|
-
if (secret != null) {
|
|
26906
|
-
found = true;
|
|
26907
|
-
try {
|
|
26908
|
-
const bytes = secret instanceof Uint8Array ? secret : Uint8Array.from(secret);
|
|
26909
|
-
return this.#options.parse(
|
|
26910
|
-
new TextDecoder("utf-8", { fatal: true }).decode(bytes)
|
|
26911
|
-
);
|
|
26912
|
-
} catch {
|
|
26913
|
-
}
|
|
26914
|
-
}
|
|
26915
|
-
} catch {
|
|
26916
|
-
}
|
|
26917
|
-
try {
|
|
26918
|
-
const serialized = await entry.getPassword();
|
|
26919
|
-
if (serialized != null) {
|
|
26920
|
-
found = true;
|
|
26921
|
-
const value = this.#options.parse(serialized);
|
|
26922
|
-
if (migrateLegacy) {
|
|
26923
|
-
try {
|
|
26924
|
-
await this.#writeEntry(entry, serialized);
|
|
26925
|
-
} catch {
|
|
26926
|
-
}
|
|
26927
|
-
}
|
|
26928
|
-
return value;
|
|
26929
|
-
}
|
|
26930
|
-
} catch {
|
|
26931
|
-
}
|
|
26932
|
-
if (found && migrateLegacy) {
|
|
26933
|
-
try {
|
|
26934
|
-
await entry.deleteCredential();
|
|
26935
|
-
} catch {
|
|
26936
|
-
}
|
|
26937
|
-
}
|
|
26938
|
-
return null;
|
|
26939
|
-
}
|
|
26940
|
-
async #writeEntry(entry, serialized) {
|
|
26941
|
-
await entry.setSecret(new TextEncoder().encode(serialized));
|
|
26942
|
-
}
|
|
26943
|
-
#warn() {
|
|
26944
|
-
if (this.#warned) return;
|
|
26945
|
-
this.#warned = true;
|
|
26946
|
-
(this.#options.warn ?? console.warn)(this.#options.warning);
|
|
26947
|
-
}
|
|
26948
|
-
};
|
|
26949
|
-
|
|
26950
|
-
// src/config-store.ts
|
|
26951
|
-
var FILE_CREDENTIAL_WARNING = "Warning: the operating-system credential store is unavailable. Glossa is using a mode-0600 credential file.";
|
|
26952
|
-
function parseCredentials(value) {
|
|
26953
|
-
let parsed;
|
|
26954
|
-
try {
|
|
26955
|
-
parsed = JSON.parse(value);
|
|
26956
|
-
} catch {
|
|
26957
|
-
throw new Error("Stored Glossa credentials are invalid.");
|
|
26958
|
-
}
|
|
26959
|
-
if (typeof parsed.issuer !== "string" || typeof parsed.clientId !== "string" || typeof parsed.audience !== "string" || typeof parsed.accessToken !== "string" || typeof parsed.expiresAt !== "string" || !Number.isFinite(Date.parse(parsed.expiresAt)) || typeof parsed.tokenType !== "string" || parsed.refreshToken !== void 0 && typeof parsed.refreshToken !== "string" || parsed.scope !== void 0 && typeof parsed.scope !== "string" || parsed.requestedScope !== void 0 && typeof parsed.requestedScope !== "string") {
|
|
26960
|
-
throw new Error("Stored Glossa credentials are invalid.");
|
|
26961
|
-
}
|
|
26962
|
-
return parsed;
|
|
26963
|
-
}
|
|
26964
|
-
var store = new SecureStore({
|
|
26965
|
-
account: "oauth",
|
|
26966
|
-
file: path2.join(configDirectory(), "credentials.json"),
|
|
26967
|
-
warning: FILE_CREDENTIAL_WARNING,
|
|
26968
|
-
parse: parseCredentials
|
|
26969
|
-
});
|
|
26970
|
-
async function saveCredentials(credentials) {
|
|
26971
|
-
return await store.save(credentials);
|
|
26972
|
-
}
|
|
26973
|
-
async function loadCredentials() {
|
|
26974
|
-
const loaded = await store.load();
|
|
26975
|
-
return loaded ? { credentials: loaded.value, backend: loaded.backend } : null;
|
|
26976
|
-
}
|
|
26977
|
-
async function peekCredentials() {
|
|
26978
|
-
const loaded = await store.peek();
|
|
26979
|
-
return loaded ? { credentials: loaded.value, backend: loaded.backend } : null;
|
|
26980
|
-
}
|
|
26981
|
-
async function deleteCredentials() {
|
|
26982
|
-
await store.delete();
|
|
26983
|
-
}
|
|
26984
|
-
|
|
26985
|
-
// src/auth-scopes.ts
|
|
26986
|
-
function normalizedScopes(value) {
|
|
26987
|
-
return [...new Set(value?.trim().split(/\s+/).filter(Boolean) ?? [])].sort();
|
|
26988
|
-
}
|
|
26989
|
-
function scopesMatch(left, right) {
|
|
26990
|
-
return JSON.stringify(normalizedScopes(left)) === JSON.stringify(normalizedScopes(right));
|
|
26991
|
-
}
|
|
26992
|
-
function grantedScopesSatisfyRequest(granted, requested, hasRefreshToken) {
|
|
26993
|
-
const grantedSet = new Set(normalizedScopes(granted));
|
|
26994
|
-
return normalizedScopes(requested).every(
|
|
26995
|
-
(scope) => grantedSet.has(scope) || scope === "offline_access" && hasRefreshToken
|
|
26996
|
-
);
|
|
26997
|
-
}
|
|
26998
|
-
|
|
26999
|
-
// src/auth-session.ts
|
|
27000
|
-
var EXPIRY_BUFFER_MS = 6e4;
|
|
27001
|
-
function endpoint(issuer, pathname) {
|
|
27002
|
-
return new URL(pathname, issuer.endsWith("/") ? issuer : `${issuer}/`).toString();
|
|
27003
|
-
}
|
|
27004
|
-
var SessionExpiredError = class extends Error {
|
|
27005
|
-
constructor() {
|
|
27006
|
-
super("Session expired. Run Glossa again to sign in.");
|
|
27007
|
-
this.name = "SessionExpiredError";
|
|
27008
|
-
}
|
|
27009
|
-
};
|
|
27010
|
-
function sessionExpiredError() {
|
|
27011
|
-
return new SessionExpiredError();
|
|
27012
|
-
}
|
|
27013
|
-
function oauthMessage(data, status) {
|
|
27014
|
-
return data.error_description ?? data.error ?? `HTTP ${status}`;
|
|
27015
|
-
}
|
|
27016
|
-
function isTokenResponse(data) {
|
|
27017
|
-
return "access_token" in data;
|
|
27018
|
-
}
|
|
27019
|
-
function accessTokenNeedsRefresh(credentials, now = Date.now()) {
|
|
27020
|
-
return Date.parse(credentials.expiresAt) <= now + EXPIRY_BUFFER_MS;
|
|
27021
|
-
}
|
|
27022
|
-
async function refreshCredentials(credentials, dependencies = {}) {
|
|
27023
|
-
if (!credentials.refreshToken) throw sessionExpiredError();
|
|
27024
|
-
const fetchRequest = dependencies.fetch ?? fetch;
|
|
27025
|
-
const remove = dependencies.deleteCredentials ?? deleteCredentials;
|
|
27026
|
-
const save = dependencies.saveCredentials ?? saveCredentials;
|
|
27027
|
-
const now = dependencies.now ?? Date.now;
|
|
27028
|
-
const response = await fetchRequest(endpoint(credentials.issuer, "oauth/token"), {
|
|
27029
|
-
method: "POST",
|
|
27030
|
-
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
27031
|
-
body: new URLSearchParams({
|
|
27032
|
-
grant_type: "refresh_token",
|
|
27033
|
-
client_id: credentials.clientId,
|
|
27034
|
-
refresh_token: credentials.refreshToken
|
|
27035
|
-
}),
|
|
27036
|
-
...dependencies.signal ? { signal: dependencies.signal } : {}
|
|
27037
|
-
});
|
|
27038
|
-
const data = await response.json();
|
|
27039
|
-
if (!response.ok || !isTokenResponse(data)) {
|
|
27040
|
-
const oauth = data;
|
|
27041
|
-
if (oauth.error === "invalid_grant") {
|
|
27042
|
-
await remove();
|
|
27043
|
-
throw sessionExpiredError();
|
|
27044
|
-
}
|
|
27045
|
-
throw new Error(oauthMessage(oauth, response.status));
|
|
27046
|
-
}
|
|
27047
|
-
const grantedScope = data.scope ?? credentials.scope;
|
|
27048
|
-
if (credentials.requestedScope && !grantedScopesSatisfyRequest(
|
|
27049
|
-
grantedScope,
|
|
27050
|
-
credentials.requestedScope,
|
|
27051
|
-
Boolean(data.refresh_token ?? credentials.refreshToken)
|
|
27052
|
-
)) {
|
|
27053
|
-
await remove();
|
|
27054
|
-
throw sessionExpiredError();
|
|
27055
|
-
}
|
|
27056
|
-
const refreshed = {
|
|
27057
|
-
...credentials,
|
|
27058
|
-
accessToken: data.access_token,
|
|
27059
|
-
refreshToken: data.refresh_token ?? credentials.refreshToken,
|
|
27060
|
-
expiresAt: new Date(now() + data.expires_in * 1e3).toISOString(),
|
|
27061
|
-
tokenType: data.token_type,
|
|
27062
|
-
...grantedScope ? { scope: grantedScope } : {}
|
|
27063
|
-
};
|
|
27064
|
-
await save(refreshed);
|
|
27065
|
-
return refreshed;
|
|
27066
|
-
}
|
|
27067
|
-
async function validCredentials(credentials, dependencies = {}) {
|
|
27068
|
-
const now = dependencies.now ?? Date.now;
|
|
27069
|
-
if (!accessTokenNeedsRefresh(credentials, now())) return credentials;
|
|
27070
|
-
return await refreshCredentials(credentials, dependencies);
|
|
27071
|
-
}
|
|
27072
|
-
function parseProfile(value) {
|
|
27073
|
-
if (!value || typeof value !== "object") {
|
|
27074
|
-
throw new Error("Auth0 returned an invalid user profile.");
|
|
27075
|
-
}
|
|
27076
|
-
const profile = value;
|
|
27077
|
-
if (typeof profile.sub !== "string" || profile.name !== void 0 && typeof profile.name !== "string" || profile.email !== void 0 && typeof profile.email !== "string") {
|
|
27078
|
-
throw new Error("Auth0 returned an invalid user profile.");
|
|
27079
|
-
}
|
|
27080
|
-
return profile;
|
|
27081
|
-
}
|
|
27082
|
-
async function requestProfile(credentials, fetchRequest, signal) {
|
|
27083
|
-
return await fetchRequest(endpoint(credentials.issuer, "userinfo"), {
|
|
27084
|
-
headers: { authorization: `${credentials.tokenType} ${credentials.accessToken}` },
|
|
27085
|
-
...signal ? { signal } : {}
|
|
27086
|
-
});
|
|
27087
|
-
}
|
|
27088
|
-
async function loadUserProfile(credentials, dependencies = {}) {
|
|
27089
|
-
const fetchRequest = dependencies.fetch ?? fetch;
|
|
27090
|
-
let current = await validCredentials(credentials, dependencies);
|
|
27091
|
-
let response = await requestProfile(current, fetchRequest, dependencies.signal);
|
|
27092
|
-
if (response.status === 401 && current.refreshToken) {
|
|
27093
|
-
current = await refreshCredentials(current, dependencies);
|
|
27094
|
-
response = await requestProfile(current, fetchRequest, dependencies.signal);
|
|
27095
|
-
}
|
|
27096
|
-
if (!response.ok) {
|
|
27097
|
-
throw new Error(`Auth0 profile request failed with HTTP ${response.status}.`);
|
|
27098
|
-
}
|
|
27099
|
-
return { credentials: current, profile: parseProfile(await response.json()) };
|
|
27100
|
-
}
|
|
27101
|
-
|
|
27102
|
-
// src/device-flow.ts
|
|
27103
|
-
import { setTimeout as delay } from "node:timers/promises";
|
|
27104
|
-
|
|
27105
|
-
// src/open-browser.ts
|
|
27106
|
-
import { spawn } from "node:child_process";
|
|
27107
|
-
async function openBrowser(url2) {
|
|
27108
|
-
const command = process.platform === "darwin" ? { file: "open", args: [url2] } : process.platform === "win32" ? { file: "cmd", args: ["/c", "start", "", url2] } : { file: "xdg-open", args: [url2] };
|
|
27109
|
-
return await new Promise((resolve) => {
|
|
27110
|
-
const child = spawn(command.file, command.args, {
|
|
27111
|
-
detached: true,
|
|
27112
|
-
stdio: "ignore",
|
|
27113
|
-
windowsHide: true
|
|
27114
|
-
});
|
|
27115
|
-
child.once("error", () => resolve(false));
|
|
27116
|
-
child.once("spawn", () => {
|
|
27117
|
-
child.unref();
|
|
27118
|
-
resolve(true);
|
|
27119
|
-
});
|
|
27120
|
-
});
|
|
27121
|
-
}
|
|
27122
|
-
|
|
27123
|
-
// src/device-flow.ts
|
|
27124
|
-
function endpoint2(issuer, pathname) {
|
|
27125
|
-
return new URL(pathname, issuer.endsWith("/") ? issuer : `${issuer}/`).toString();
|
|
27126
|
-
}
|
|
27127
|
-
function canceledError() {
|
|
27128
|
-
return new Error("Login canceled.");
|
|
27129
|
-
}
|
|
27130
|
-
function assertActive(signal) {
|
|
27131
|
-
if (signal?.aborted) throw canceledError();
|
|
27132
|
-
}
|
|
27133
|
-
async function defaultDelay(milliseconds, signal) {
|
|
27134
|
-
await delay(milliseconds, void 0, signal ? { signal } : void 0);
|
|
27135
|
-
}
|
|
27136
|
-
async function postForm(fetchRequest, url2, values, signal) {
|
|
27137
|
-
const response = await fetchRequest(url2, {
|
|
27138
|
-
method: "POST",
|
|
27139
|
-
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
27140
|
-
body: new URLSearchParams(values),
|
|
27141
|
-
...signal ? { signal } : {}
|
|
27142
|
-
});
|
|
27143
|
-
const data = await response.json();
|
|
27144
|
-
if (!response.ok) {
|
|
27145
|
-
const oauth = data;
|
|
27146
|
-
throw new Error(oauth.error_description ?? oauth.error ?? `HTTP ${response.status}`);
|
|
27147
|
-
}
|
|
27148
|
-
return data;
|
|
27149
|
-
}
|
|
27150
|
-
async function loginWithDeviceFlow(options, dependencies = {}) {
|
|
27151
|
-
const fetchRequest = dependencies.fetch ?? fetch;
|
|
27152
|
-
const wait = dependencies.delay ?? defaultDelay;
|
|
27153
|
-
const browse = dependencies.openBrowser ?? openBrowser;
|
|
27154
|
-
const save = dependencies.saveCredentials ?? saveCredentials;
|
|
27155
|
-
const now = dependencies.now ?? Date.now;
|
|
27156
|
-
const log = dependencies.log ?? console.log;
|
|
27157
|
-
try {
|
|
27158
|
-
assertActive(options.signal);
|
|
27159
|
-
const code = await postForm(
|
|
27160
|
-
fetchRequest,
|
|
27161
|
-
endpoint2(options.issuer, "oauth/device/code"),
|
|
27162
|
-
{
|
|
27163
|
-
client_id: options.clientId,
|
|
27164
|
-
audience: options.audience,
|
|
27165
|
-
scope: options.scope
|
|
27166
|
-
},
|
|
27167
|
-
options.signal
|
|
27168
|
-
);
|
|
27169
|
-
const verificationUrl = code.verification_uri_complete ?? code.verification_uri;
|
|
27170
|
-
const opened = await browse(verificationUrl);
|
|
27171
|
-
log(opened ? "Opened your browser for Glossa login." : "Open this URL to sign in:");
|
|
27172
|
-
log(verificationUrl);
|
|
27173
|
-
if (!code.verification_uri_complete) log(`Code: ${code.user_code}`);
|
|
27174
|
-
const startedAt = now();
|
|
27175
|
-
let intervalSeconds = Math.max(code.interval ?? 5, 1);
|
|
27176
|
-
while (now() - startedAt < code.expires_in * 1e3) {
|
|
27177
|
-
assertActive(options.signal);
|
|
27178
|
-
await wait(intervalSeconds * 1e3, options.signal);
|
|
27179
|
-
assertActive(options.signal);
|
|
27180
|
-
const response = await fetchRequest(endpoint2(options.issuer, "oauth/token"), {
|
|
27181
|
-
method: "POST",
|
|
27182
|
-
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
27183
|
-
body: new URLSearchParams({
|
|
27184
|
-
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
27185
|
-
device_code: code.device_code,
|
|
27186
|
-
client_id: options.clientId
|
|
27187
|
-
}),
|
|
27188
|
-
...options.signal ? { signal: options.signal } : {}
|
|
27189
|
-
});
|
|
27190
|
-
const data = await response.json();
|
|
27191
|
-
if (response.ok && "access_token" in data) {
|
|
27192
|
-
if (!data.refresh_token) {
|
|
27193
|
-
throw new Error("Auth0 did not issue a refresh token.");
|
|
27194
|
-
}
|
|
27195
|
-
const grantedScope = data.scope ?? options.scope;
|
|
27196
|
-
if (!grantedScopesSatisfyRequest(grantedScope, options.scope, true)) {
|
|
27197
|
-
throw new Error("Auth0 did not grant the permissions Glossa requires.");
|
|
27198
|
-
}
|
|
27199
|
-
await save({
|
|
27200
|
-
issuer: options.issuer,
|
|
27201
|
-
clientId: options.clientId,
|
|
27202
|
-
audience: options.audience,
|
|
27203
|
-
accessToken: data.access_token,
|
|
27204
|
-
refreshToken: data.refresh_token,
|
|
27205
|
-
expiresAt: new Date(now() + data.expires_in * 1e3).toISOString(),
|
|
27206
|
-
tokenType: data.token_type,
|
|
27207
|
-
scope: grantedScope,
|
|
27208
|
-
requestedScope: options.scope
|
|
27209
|
-
});
|
|
27210
|
-
log("Signed in to Glossa.");
|
|
27211
|
-
return;
|
|
27212
|
-
}
|
|
27213
|
-
const error46 = data;
|
|
27214
|
-
if (error46.error === "authorization_pending") continue;
|
|
27215
|
-
if (error46.error === "slow_down") {
|
|
27216
|
-
intervalSeconds += 5;
|
|
27217
|
-
continue;
|
|
27218
|
-
}
|
|
27219
|
-
if (error46.error === "access_denied") throw new Error("Login was denied.");
|
|
27220
|
-
if (error46.error === "expired_token") throw new Error("The login code expired.");
|
|
27221
|
-
throw new Error(error46.error_description ?? error46.error);
|
|
27222
|
-
}
|
|
27223
|
-
throw new Error("The login code expired.");
|
|
27224
|
-
} catch (error46) {
|
|
27225
|
-
if (options.signal?.aborted || error46 instanceof Error && error46.name === "AbortError") {
|
|
27226
|
-
throw canceledError();
|
|
27227
|
-
}
|
|
27228
|
-
throw error46;
|
|
27229
|
-
}
|
|
27230
|
-
}
|
|
27231
|
-
|
|
27232
|
-
// src/auth-login.ts
|
|
27233
|
-
function normalizedIssuer(value) {
|
|
27234
|
-
return value.replace(/\/+$/, "");
|
|
27235
|
-
}
|
|
27236
|
-
function credentialsMatchLoginOptions(credentials, options) {
|
|
27237
|
-
return normalizedIssuer(credentials.issuer) === normalizedIssuer(options.issuer) && credentials.clientId === options.clientId && credentials.audience === options.audience && scopesMatch(credentials.requestedScope, options.scope) && grantedScopesSatisfyRequest(
|
|
27238
|
-
credentials.scope,
|
|
27239
|
-
options.scope,
|
|
27240
|
-
Boolean(credentials.refreshToken)
|
|
27241
|
-
);
|
|
27242
|
-
}
|
|
27243
|
-
async function signedInSession(options, dependencies = {}) {
|
|
27244
|
-
const load = dependencies.loadCredentials ?? loadCredentials;
|
|
27245
|
-
const validate2 = dependencies.validCredentials ?? validCredentials;
|
|
27246
|
-
const login = dependencies.loginWithDeviceFlow ?? loginWithDeviceFlow;
|
|
27247
|
-
const loaded = await load();
|
|
27248
|
-
if (loaded && credentialsMatchLoginOptions(loaded.credentials, options)) {
|
|
27249
|
-
try {
|
|
27250
|
-
const credentials = await validate2(
|
|
27251
|
-
loaded.credentials,
|
|
27252
|
-
options.signal ? { signal: options.signal } : {}
|
|
27253
|
-
);
|
|
27254
|
-
return { credentials, loginPerformed: false };
|
|
27255
|
-
} catch (error46) {
|
|
27256
|
-
if (!(error46 instanceof SessionExpiredError)) throw error46;
|
|
27257
|
-
}
|
|
27258
|
-
}
|
|
27259
|
-
await login(options);
|
|
27260
|
-
const completed = await load();
|
|
27261
|
-
if (!completed) throw new Error("Glossa could not load the completed login.");
|
|
27262
|
-
return {
|
|
27263
|
-
credentials: await validate2(
|
|
27264
|
-
completed.credentials,
|
|
27265
|
-
options.signal ? { signal: options.signal } : {}
|
|
27266
|
-
),
|
|
27267
|
-
loginPerformed: true
|
|
27268
|
-
};
|
|
27269
|
-
}
|
|
27270
|
-
|
|
27271
26748
|
// ../../node_modules/zod/v4/classic/external.js
|
|
27272
26749
|
var external_exports = {};
|
|
27273
26750
|
__export(external_exports, {
|
|
@@ -27999,10 +27476,10 @@ function mergeDefs(...defs) {
|
|
|
27999
27476
|
function cloneDef(schema) {
|
|
28000
27477
|
return mergeDefs(schema._zod.def);
|
|
28001
27478
|
}
|
|
28002
|
-
function getElementAtPath(obj,
|
|
28003
|
-
if (!
|
|
27479
|
+
function getElementAtPath(obj, path12) {
|
|
27480
|
+
if (!path12)
|
|
28004
27481
|
return obj;
|
|
28005
|
-
return
|
|
27482
|
+
return path12.reduce((acc, key) => acc?.[key], obj);
|
|
28006
27483
|
}
|
|
28007
27484
|
function promiseAllObject(promisesObj) {
|
|
28008
27485
|
const keys = Object.keys(promisesObj);
|
|
@@ -28363,11 +27840,11 @@ function aborted(x, startIndex = 0) {
|
|
|
28363
27840
|
}
|
|
28364
27841
|
return false;
|
|
28365
27842
|
}
|
|
28366
|
-
function prefixIssues(
|
|
27843
|
+
function prefixIssues(path12, issues) {
|
|
28367
27844
|
return issues.map((iss) => {
|
|
28368
27845
|
var _a;
|
|
28369
27846
|
(_a = iss).path ?? (_a.path = []);
|
|
28370
|
-
iss.path.unshift(
|
|
27847
|
+
iss.path.unshift(path12);
|
|
28371
27848
|
return iss;
|
|
28372
27849
|
});
|
|
28373
27850
|
}
|
|
@@ -28529,7 +28006,7 @@ function formatError(error46, mapper = (issue2) => issue2.message) {
|
|
|
28529
28006
|
}
|
|
28530
28007
|
function treeifyError(error46, mapper = (issue2) => issue2.message) {
|
|
28531
28008
|
const result = { errors: [] };
|
|
28532
|
-
const processError = (error47,
|
|
28009
|
+
const processError = (error47, path12 = []) => {
|
|
28533
28010
|
var _a, _b;
|
|
28534
28011
|
for (const issue2 of error47.issues) {
|
|
28535
28012
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -28539,7 +28016,7 @@ function treeifyError(error46, mapper = (issue2) => issue2.message) {
|
|
|
28539
28016
|
} else if (issue2.code === "invalid_element") {
|
|
28540
28017
|
processError({ issues: issue2.issues }, issue2.path);
|
|
28541
28018
|
} else {
|
|
28542
|
-
const fullpath = [...
|
|
28019
|
+
const fullpath = [...path12, ...issue2.path];
|
|
28543
28020
|
if (fullpath.length === 0) {
|
|
28544
28021
|
result.errors.push(mapper(issue2));
|
|
28545
28022
|
continue;
|
|
@@ -28571,8 +28048,8 @@ function treeifyError(error46, mapper = (issue2) => issue2.message) {
|
|
|
28571
28048
|
}
|
|
28572
28049
|
function toDotPath(_path) {
|
|
28573
28050
|
const segs = [];
|
|
28574
|
-
const
|
|
28575
|
-
for (const seg of
|
|
28051
|
+
const path12 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
28052
|
+
for (const seg of path12) {
|
|
28576
28053
|
if (typeof seg === "number")
|
|
28577
28054
|
segs.push(`[${seg}]`);
|
|
28578
28055
|
else if (typeof seg === "symbol")
|
|
@@ -40122,9 +39599,12 @@ var UsageError = class extends Error {
|
|
|
40122
39599
|
};
|
|
40123
39600
|
var retiredCommands = /* @__PURE__ */ new Set([
|
|
40124
39601
|
"completions",
|
|
39602
|
+
"devices",
|
|
40125
39603
|
"doctor",
|
|
40126
39604
|
"login",
|
|
40127
|
-
"
|
|
39605
|
+
"logout",
|
|
39606
|
+
"start",
|
|
39607
|
+
"status"
|
|
40128
39608
|
]);
|
|
40129
39609
|
function parseWorkspace(args) {
|
|
40130
39610
|
let selectedPath;
|
|
@@ -40182,12 +39662,6 @@ function parseWorkspace(args) {
|
|
|
40182
39662
|
accessProfile
|
|
40183
39663
|
};
|
|
40184
39664
|
}
|
|
40185
|
-
function parseDevices(args) {
|
|
40186
|
-
if (args[0] === "revoke" && args.length === 2) {
|
|
40187
|
-
return { command: "devices", action: "revoke", deviceId: args[1] };
|
|
40188
|
-
}
|
|
40189
|
-
throw new UsageError("Use: glossa devices revoke <id>.");
|
|
40190
|
-
}
|
|
40191
39665
|
function parseUpdate(args) {
|
|
40192
39666
|
if (args.length === 0) return { command: "update", action: "install" };
|
|
40193
39667
|
let check3 = false;
|
|
@@ -40247,15 +39721,6 @@ function parseInvocation(args) {
|
|
|
40247
39721
|
return { command: "version" };
|
|
40248
39722
|
}
|
|
40249
39723
|
if (command === "--") return parseWorkspace(args);
|
|
40250
|
-
if (command === "status") {
|
|
40251
|
-
noOptions("Status", options);
|
|
40252
|
-
return { command };
|
|
40253
|
-
}
|
|
40254
|
-
if (command === "devices") return parseDevices(options);
|
|
40255
|
-
if (command === "logout") {
|
|
40256
|
-
noOptions("Logout", options);
|
|
40257
|
-
return { command };
|
|
40258
|
-
}
|
|
40259
39724
|
if (command === "unpair") {
|
|
40260
39725
|
noOptions("Unpair", options);
|
|
40261
39726
|
return { command };
|
|
@@ -40284,47 +39749,9 @@ function formatRelativeTime(iso, now = Date.now()) {
|
|
|
40284
39749
|
const days = Math.round(hours / 24);
|
|
40285
39750
|
return `${days}d ago`;
|
|
40286
39751
|
}
|
|
40287
|
-
function formatDeviceRow(device, now = Date.now()) {
|
|
40288
|
-
const platform2 = device.platform ?? "unknown platform";
|
|
40289
|
-
return `${device.id} ${device.name} ${platform2} last seen ${formatRelativeTime(device.lastSeenAt, now)} ${deviceStatus(device)}`;
|
|
40290
|
-
}
|
|
40291
|
-
|
|
40292
|
-
// src/logout.ts
|
|
40293
|
-
function browserLogoutUrl(issuer) {
|
|
40294
|
-
return new URL(
|
|
40295
|
-
"v2/logout",
|
|
40296
|
-
issuer.endsWith("/") ? issuer : `${issuer}/`
|
|
40297
|
-
).toString();
|
|
40298
|
-
}
|
|
40299
|
-
async function logoutFromGlossa(dependencies = {}) {
|
|
40300
|
-
const remove = dependencies.deleteCredentials ?? deleteCredentials;
|
|
40301
|
-
const peek = dependencies.peekCredentials ?? peekCredentials;
|
|
40302
|
-
const browse = dependencies.openBrowser ?? openBrowser;
|
|
40303
|
-
const log = dependencies.log ?? console.log;
|
|
40304
|
-
let stored = null;
|
|
40305
|
-
let present = true;
|
|
40306
|
-
try {
|
|
40307
|
-
stored = await peek();
|
|
40308
|
-
present = stored !== null;
|
|
40309
|
-
} catch {
|
|
40310
|
-
}
|
|
40311
|
-
const issuer = dependencies.issuer ?? stored?.credentials.issuer;
|
|
40312
|
-
await remove();
|
|
40313
|
-
log(
|
|
40314
|
-
present ? "Signed out of the Glossa account. Computer pairing stays active." : "Already signed out of the Glossa account. Computer pairing stays active."
|
|
40315
|
-
);
|
|
40316
|
-
const url2 = browserLogoutUrl(issuer ?? loadAuthConfig().issuer);
|
|
40317
|
-
const opened = await browse(url2);
|
|
40318
|
-
if (opened) {
|
|
40319
|
-
log("Opened Glossa browser sign-out.");
|
|
40320
|
-
} else {
|
|
40321
|
-
log("Open this URL to finish signing out in your browser:");
|
|
40322
|
-
log(url2);
|
|
40323
|
-
}
|
|
40324
|
-
}
|
|
40325
39752
|
|
|
40326
39753
|
// src/relay-client.ts
|
|
40327
|
-
import
|
|
39754
|
+
import os from "node:os";
|
|
40328
39755
|
var DEFAULT_RELAY_ORIGIN = "https://mcp.glossa.sh";
|
|
40329
39756
|
function isLoopback(hostname3) {
|
|
40330
39757
|
return hostname3 === "localhost" || hostname3 === "127.0.0.1" || hostname3 === "[::1]";
|
|
@@ -40368,16 +39795,61 @@ function loadRelayEndpoints(environment = process.env) {
|
|
|
40368
39795
|
return { relayOrigin, workerOrigin };
|
|
40369
39796
|
}
|
|
40370
39797
|
function defaultDeviceName() {
|
|
40371
|
-
return deviceNameSchema.parse(
|
|
39798
|
+
return deviceNameSchema.parse(os.hostname());
|
|
40372
39799
|
}
|
|
40373
|
-
var
|
|
40374
|
-
constructor() {
|
|
40375
|
-
super("The Glossa pairing code expired. Run Glossa again to get a new code.");
|
|
40376
|
-
this.name = "DevicePairingExpiredError";
|
|
40377
|
-
}
|
|
39800
|
+
var PairingCodeExpiredError = class extends Error {
|
|
40378
39801
|
};
|
|
40379
|
-
function
|
|
40380
|
-
if (status ===
|
|
39802
|
+
function pairingHttpError(status) {
|
|
39803
|
+
if (status === 429) {
|
|
39804
|
+
return new Error("Glossa pairing is rate limited. Try again later.");
|
|
39805
|
+
}
|
|
39806
|
+
return new Error(`The Glossa relay returned HTTP ${status}.`);
|
|
39807
|
+
}
|
|
39808
|
+
async function createPairing(endpoints, deviceName, platform2, fetchRequest = fetch) {
|
|
39809
|
+
const response = await fetchRequest(`${endpoints.relayOrigin}/v1/pairings`, {
|
|
39810
|
+
method: "POST",
|
|
39811
|
+
headers: { "content-type": "application/json" },
|
|
39812
|
+
body: JSON.stringify({ name: deviceNameSchema.parse(deviceName), platform: platform2 })
|
|
39813
|
+
});
|
|
39814
|
+
let data = {};
|
|
39815
|
+
try {
|
|
39816
|
+
data = await response.json();
|
|
39817
|
+
} catch {
|
|
39818
|
+
}
|
|
39819
|
+
if (!response.ok) throw pairingHttpError(response.status);
|
|
39820
|
+
if (typeof data.code !== "string" || typeof data.expiresAt !== "string" || Number.isNaN(Date.parse(data.expiresAt))) {
|
|
39821
|
+
throw new Error("The Glossa relay returned an invalid pairing response.");
|
|
39822
|
+
}
|
|
39823
|
+
return { code: data.code, expiresAt: data.expiresAt };
|
|
39824
|
+
}
|
|
39825
|
+
async function redeemPairing(endpoints, code, fetchRequest = fetch) {
|
|
39826
|
+
const response = await fetchRequest(`${endpoints.relayOrigin}/v1/pairings/redeem`, {
|
|
39827
|
+
method: "POST",
|
|
39828
|
+
headers: { "content-type": "application/json" },
|
|
39829
|
+
body: JSON.stringify({ code })
|
|
39830
|
+
});
|
|
39831
|
+
let data = {};
|
|
39832
|
+
try {
|
|
39833
|
+
data = await response.json();
|
|
39834
|
+
} catch {
|
|
39835
|
+
}
|
|
39836
|
+
if (response.status === 202) return "pending";
|
|
39837
|
+
if (response.status === 404) {
|
|
39838
|
+
throw new PairingCodeExpiredError(
|
|
39839
|
+
"The pairing code expired. Glossa will show a fresh one."
|
|
39840
|
+
);
|
|
39841
|
+
}
|
|
39842
|
+
if (!response.ok) throw pairingHttpError(response.status);
|
|
39843
|
+
if (typeof data.device?.id !== "string" || typeof data.device.name !== "string" || typeof data.device_token !== "string") {
|
|
39844
|
+
throw new Error("The Glossa relay returned an invalid pairing response.");
|
|
39845
|
+
}
|
|
39846
|
+
return {
|
|
39847
|
+
device: { id: data.device.id, name: data.device.name },
|
|
39848
|
+
token: data.device_token
|
|
39849
|
+
};
|
|
39850
|
+
}
|
|
39851
|
+
function relayError(status, data, unauthorized) {
|
|
39852
|
+
if (status === 401) return new Error(unauthorized);
|
|
40381
39853
|
if (status === 403 && data.error === "account_disabled") {
|
|
40382
39854
|
return new Error("This Glossa account is disabled.");
|
|
40383
39855
|
}
|
|
@@ -40400,7 +39872,7 @@ function relayError(status, data) {
|
|
|
40400
39872
|
function validNullableString(value) {
|
|
40401
39873
|
return value === null || typeof value === "string";
|
|
40402
39874
|
}
|
|
40403
|
-
function
|
|
39875
|
+
function parseDevices(value) {
|
|
40404
39876
|
if (!Array.isArray(value)) {
|
|
40405
39877
|
throw new Error("The Glossa relay returned an invalid device list response.");
|
|
40406
39878
|
}
|
|
@@ -40415,82 +39887,18 @@ function parseDevices2(value) {
|
|
|
40415
39887
|
activeWorkers: device.activeWorkers ?? null
|
|
40416
39888
|
}));
|
|
40417
39889
|
}
|
|
40418
|
-
|
|
39890
|
+
var PAIRING_REJECTED = "This computer's pairing is no longer valid. Run Glossa again to pair it.";
|
|
39891
|
+
async function listDevices(endpoints, authorization, fetchRequest = fetch) {
|
|
40419
39892
|
const response = await fetchRequest(`${endpoints.relayOrigin}/v1/devices`, {
|
|
40420
|
-
headers: {
|
|
40421
|
-
authorization: `${credentials.tokenType} ${credentials.accessToken}`
|
|
40422
|
-
}
|
|
40423
|
-
});
|
|
40424
|
-
let data = {};
|
|
40425
|
-
try {
|
|
40426
|
-
data = await response.json();
|
|
40427
|
-
} catch {
|
|
40428
|
-
}
|
|
40429
|
-
if (!response.ok) throw relayError(response.status, data);
|
|
40430
|
-
return parseDevices2(data.devices);
|
|
40431
|
-
}
|
|
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}` })
|
|
39893
|
+
headers: { authorization }
|
|
40438
39894
|
});
|
|
40439
39895
|
let data = {};
|
|
40440
39896
|
try {
|
|
40441
39897
|
data = await response.json();
|
|
40442
39898
|
} catch {
|
|
40443
39899
|
}
|
|
40444
|
-
if (!response.ok)
|
|
40445
|
-
|
|
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
|
-
};
|
|
40460
|
-
}
|
|
40461
|
-
async function completeDevicePairing(endpoints, pairing, fetchRequest = fetch) {
|
|
40462
|
-
const response = await fetchRequest(
|
|
40463
|
-
`${endpoints.relayOrigin}/v1/device-pairings/${encodeURIComponent(pairing.pairingId)}/complete`,
|
|
40464
|
-
{
|
|
40465
|
-
method: "POST",
|
|
40466
|
-
headers: { "content-type": "application/json" },
|
|
40467
|
-
body: JSON.stringify({ pairing_secret: pairing.pairingSecret })
|
|
40468
|
-
}
|
|
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
|
-
};
|
|
39900
|
+
if (!response.ok) throw relayError(response.status, data, PAIRING_REJECTED);
|
|
39901
|
+
return parseDevices(data.devices);
|
|
40494
39902
|
}
|
|
40495
39903
|
async function revokePairedDevice(endpoints, device, fetchRequest = fetch) {
|
|
40496
39904
|
const response = await fetchRequest(`${endpoints.relayOrigin}/device`, {
|
|
@@ -40503,103 +39911,17 @@ async function revokePairedDevice(endpoints, device, fetchRequest = fetch) {
|
|
|
40503
39911
|
}
|
|
40504
39912
|
throw new Error(`The Glossa relay returned HTTP ${response.status}.`);
|
|
40505
39913
|
}
|
|
40506
|
-
async function revokeDevice(endpoints,
|
|
39914
|
+
async function revokeDevice(endpoints, authorization, deviceId, fetchRequest = fetch) {
|
|
40507
39915
|
const response = await fetchRequest(`${endpoints.relayOrigin}/v1/devices/${encodeURIComponent(deviceId)}`, {
|
|
40508
39916
|
method: "DELETE",
|
|
40509
|
-
headers: {
|
|
40510
|
-
authorization: `${credentials.tokenType} ${credentials.accessToken}`
|
|
40511
|
-
}
|
|
39917
|
+
headers: { authorization }
|
|
40512
39918
|
});
|
|
40513
39919
|
if (!response.ok) {
|
|
40514
39920
|
const data = await response.json().catch(() => ({}));
|
|
40515
|
-
throw relayError(response.status, data);
|
|
40516
|
-
}
|
|
40517
|
-
}
|
|
40518
|
-
|
|
40519
|
-
// src/status-display.ts
|
|
40520
|
-
function formatStatus(status) {
|
|
40521
|
-
const lines = [
|
|
40522
|
-
`Signed in as ${status.account}.`,
|
|
40523
|
-
`Relay connected: ${status.relay}`
|
|
40524
|
-
];
|
|
40525
|
-
if (status.activeWorkers === null) {
|
|
40526
|
-
lines.push("Active workspaces: unavailable");
|
|
40527
|
-
} else if (status.activeWorkers === 0) {
|
|
40528
|
-
lines.push("No active workspaces. Run glossa from the project folder you want to expose.");
|
|
40529
|
-
} else {
|
|
40530
|
-
lines.push(`Active workspaces: ${status.activeWorkers}`);
|
|
39921
|
+
throw relayError(response.status, data, PAIRING_REJECTED);
|
|
40531
39922
|
}
|
|
40532
|
-
if (status.devices.length === 0) lines.push("No active devices.");
|
|
40533
|
-
else lines.push(...status.devices.map((device) => formatDeviceRow(device)));
|
|
40534
|
-
return lines;
|
|
40535
39923
|
}
|
|
40536
39924
|
|
|
40537
|
-
// src/status-service.ts
|
|
40538
|
-
function accountLabel(profile) {
|
|
40539
|
-
return profile.email ?? profile.name ?? profile.sub;
|
|
40540
|
-
}
|
|
40541
|
-
function activeWorkerCount(devices) {
|
|
40542
|
-
if (devices.some((device) => device.activeWorkers === null)) return null;
|
|
40543
|
-
return devices.reduce((sum, device) => sum + device.activeWorkers, 0);
|
|
40544
|
-
}
|
|
40545
|
-
var WorkspaceStatusService = class {
|
|
40546
|
-
constructor(credentials, endpoints, dependencies = {}) {
|
|
40547
|
-
this.endpoints = endpoints;
|
|
40548
|
-
this.dependencies = dependencies;
|
|
40549
|
-
this.#credentials = credentials;
|
|
40550
|
-
}
|
|
40551
|
-
endpoints;
|
|
40552
|
-
dependencies;
|
|
40553
|
-
#credentials;
|
|
40554
|
-
#inFlight;
|
|
40555
|
-
async refresh(signal) {
|
|
40556
|
-
if (this.#inFlight) return await this.#inFlight;
|
|
40557
|
-
const pending = this.#load(signal);
|
|
40558
|
-
this.#inFlight = pending;
|
|
40559
|
-
try {
|
|
40560
|
-
return await pending;
|
|
40561
|
-
} finally {
|
|
40562
|
-
if (this.#inFlight === pending) this.#inFlight = void 0;
|
|
40563
|
-
}
|
|
40564
|
-
}
|
|
40565
|
-
async #load(signal) {
|
|
40566
|
-
const validate2 = this.dependencies.validCredentials ?? validCredentials;
|
|
40567
|
-
const devicesForAccount = this.dependencies.listDevices ?? listDevices;
|
|
40568
|
-
const profileForAccount = this.dependencies.loadUserProfile ?? loadUserProfile;
|
|
40569
|
-
const baseFetch = this.dependencies.fetch ?? fetch;
|
|
40570
|
-
const fetchRequest = signal ? async (input, init) => await baseFetch(input, { ...init, signal }) : baseFetch;
|
|
40571
|
-
this.#credentials = await validate2(
|
|
40572
|
-
this.#credentials,
|
|
40573
|
-
signal ? { signal } : {}
|
|
40574
|
-
);
|
|
40575
|
-
const requestCredentials = this.#credentials;
|
|
40576
|
-
const profileRequest = profileForAccount(
|
|
40577
|
-
requestCredentials,
|
|
40578
|
-
signal ? { signal, fetch: fetchRequest } : { fetch: fetchRequest }
|
|
40579
|
-
).catch((error46) => {
|
|
40580
|
-
if (signal?.aborted) throw error46;
|
|
40581
|
-
return null;
|
|
40582
|
-
});
|
|
40583
|
-
const devicesRequest = devicesForAccount(
|
|
40584
|
-
this.endpoints,
|
|
40585
|
-
requestCredentials,
|
|
40586
|
-
fetchRequest
|
|
40587
|
-
);
|
|
40588
|
-
const [profile, devices] = await Promise.all([
|
|
40589
|
-
profileRequest,
|
|
40590
|
-
devicesRequest
|
|
40591
|
-
]);
|
|
40592
|
-
if (profile) this.#credentials = profile.credentials;
|
|
40593
|
-
const activeDevices = devices.filter((device) => device.revokedAt === null);
|
|
40594
|
-
return {
|
|
40595
|
-
account: profile ? accountLabel(profile.profile) : "Account unavailable",
|
|
40596
|
-
relay: this.endpoints.relayOrigin,
|
|
40597
|
-
activeWorkers: activeWorkerCount(activeDevices),
|
|
40598
|
-
devices: activeDevices
|
|
40599
|
-
};
|
|
40600
|
-
}
|
|
40601
|
-
};
|
|
40602
|
-
|
|
40603
39925
|
// src/ui-hud.tsx
|
|
40604
39926
|
var import_react34 = __toESM(require_react(), 1);
|
|
40605
39927
|
|
|
@@ -40753,7 +40075,7 @@ __export(base_exports, {
|
|
|
40753
40075
|
synchronizedOutput: () => synchronizedOutput
|
|
40754
40076
|
});
|
|
40755
40077
|
import process2 from "node:process";
|
|
40756
|
-
import
|
|
40078
|
+
import os2 from "node:os";
|
|
40757
40079
|
|
|
40758
40080
|
// ../../node_modules/environment/index.js
|
|
40759
40081
|
var isBrowser = globalThis.window?.document !== void 0;
|
|
@@ -40852,7 +40174,7 @@ var isOldWindows = () => {
|
|
|
40852
40174
|
if (isBrowser || !isWindows2) {
|
|
40853
40175
|
return false;
|
|
40854
40176
|
}
|
|
40855
|
-
const parts =
|
|
40177
|
+
const parts = os2.release().split(".");
|
|
40856
40178
|
const major = Number(parts[0]);
|
|
40857
40179
|
const build = Number(parts[2] ?? 0);
|
|
40858
40180
|
if (major < 10) {
|
|
@@ -45848,7 +45170,7 @@ var ansi_styles_default2 = ansiStyles2;
|
|
|
45848
45170
|
|
|
45849
45171
|
// ../../node_modules/chalk/source/vendor/supports-color/index.js
|
|
45850
45172
|
import process5 from "node:process";
|
|
45851
|
-
import
|
|
45173
|
+
import os3 from "node:os";
|
|
45852
45174
|
import tty2 from "node:tty";
|
|
45853
45175
|
function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process5.argv) {
|
|
45854
45176
|
const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
|
|
@@ -45913,7 +45235,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
|
|
|
45913
45235
|
return min;
|
|
45914
45236
|
}
|
|
45915
45237
|
if (process5.platform === "win32") {
|
|
45916
|
-
const osRelease =
|
|
45238
|
+
const osRelease = os3.release().split(".");
|
|
45917
45239
|
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
45918
45240
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
45919
45241
|
}
|
|
@@ -47689,8 +47011,8 @@ function Text({ color, backgroundColor, dimColor = false, bold = false, italic =
|
|
|
47689
47011
|
}
|
|
47690
47012
|
|
|
47691
47013
|
// ../../node_modules/ink/build/components/ErrorOverview.js
|
|
47692
|
-
var cleanupPath = (
|
|
47693
|
-
return
|
|
47014
|
+
var cleanupPath = (path12) => {
|
|
47015
|
+
return path12?.replace(`file://${cwd()}/`, "");
|
|
47694
47016
|
};
|
|
47695
47017
|
var stackUtils = new import_stack_utils.default({
|
|
47696
47018
|
cwd: cwd(),
|
|
@@ -47864,7 +47186,7 @@ function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, ex
|
|
|
47864
47186
|
for (const subscriber of animationSubscribersRef.current.values()) {
|
|
47865
47187
|
nextDueTime = Math.min(nextDueTime, subscriber.nextDueTime);
|
|
47866
47188
|
}
|
|
47867
|
-
const
|
|
47189
|
+
const delay5 = Math.max(0, nextDueTime - performance.now());
|
|
47868
47190
|
animationTimerRef.current = setTimeout(() => {
|
|
47869
47191
|
animationTimerRef.current = void 0;
|
|
47870
47192
|
const currentTime = performance.now();
|
|
@@ -47878,7 +47200,7 @@ function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, ex
|
|
|
47878
47200
|
subscriber.nextDueTime = subscriber.startTime + elapsedFrames * subscriber.interval;
|
|
47879
47201
|
}
|
|
47880
47202
|
scheduleAnimationTick();
|
|
47881
|
-
},
|
|
47203
|
+
}, delay5);
|
|
47882
47204
|
}, [clearAnimationTimer]);
|
|
47883
47205
|
const animationSubscribe = (0, import_react15.useCallback)((callback, interval) => {
|
|
47884
47206
|
const startTime = performance.now();
|
|
@@ -49792,14 +49114,136 @@ var import_react33 = __toESM(require_react(), 1);
|
|
|
49792
49114
|
|
|
49793
49115
|
// src/first-run.ts
|
|
49794
49116
|
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
49795
|
-
import
|
|
49117
|
+
import path2 from "node:path";
|
|
49118
|
+
|
|
49119
|
+
// src/secure-store.ts
|
|
49120
|
+
import { chmod, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
49121
|
+
import os4 from "node:os";
|
|
49122
|
+
import path from "node:path";
|
|
49123
|
+
var KEYRING_SERVICE = "Glossa";
|
|
49124
|
+
function configDirectory() {
|
|
49125
|
+
if (process.platform === "win32") {
|
|
49126
|
+
return path.join(process.env.APPDATA ?? os4.homedir(), "Glossa");
|
|
49127
|
+
}
|
|
49128
|
+
return path.join(
|
|
49129
|
+
process.env.XDG_CONFIG_HOME ?? path.join(os4.homedir(), ".config"),
|
|
49130
|
+
"glossa"
|
|
49131
|
+
);
|
|
49132
|
+
}
|
|
49133
|
+
var SecureStore = class {
|
|
49134
|
+
#options;
|
|
49135
|
+
#warned = false;
|
|
49136
|
+
constructor(options) {
|
|
49137
|
+
this.#options = options;
|
|
49138
|
+
}
|
|
49139
|
+
async save(value) {
|
|
49140
|
+
const serialized = JSON.stringify(value);
|
|
49141
|
+
const entry = await this.#entry();
|
|
49142
|
+
if (entry) {
|
|
49143
|
+
try {
|
|
49144
|
+
await this.#writeEntry(entry, serialized);
|
|
49145
|
+
await rm(this.#options.file, { force: true });
|
|
49146
|
+
return "keyring";
|
|
49147
|
+
} catch {
|
|
49148
|
+
}
|
|
49149
|
+
}
|
|
49150
|
+
this.#warn();
|
|
49151
|
+
await mkdir(path.dirname(this.#options.file), {
|
|
49152
|
+
recursive: true,
|
|
49153
|
+
mode: 448
|
|
49154
|
+
});
|
|
49155
|
+
await writeFile(
|
|
49156
|
+
this.#options.file,
|
|
49157
|
+
`${JSON.stringify(value, null, 2)}
|
|
49158
|
+
`,
|
|
49159
|
+
{ encoding: "utf8", mode: 384 }
|
|
49160
|
+
);
|
|
49161
|
+
if (process.platform !== "win32") {
|
|
49162
|
+
await chmod(this.#options.file, 384);
|
|
49163
|
+
}
|
|
49164
|
+
return "file";
|
|
49165
|
+
}
|
|
49166
|
+
async load() {
|
|
49167
|
+
const entry = await this.#entry();
|
|
49168
|
+
if (entry) {
|
|
49169
|
+
const value2 = await this.#readEntry(entry);
|
|
49170
|
+
if (value2 != null) return { value: value2, backend: "keyring" };
|
|
49171
|
+
}
|
|
49172
|
+
let value;
|
|
49173
|
+
try {
|
|
49174
|
+
value = this.#options.parse(await readFile(this.#options.file, "utf8"));
|
|
49175
|
+
} catch (error46) {
|
|
49176
|
+
if (error46.code === "ENOENT") return null;
|
|
49177
|
+
throw error46;
|
|
49178
|
+
}
|
|
49179
|
+
this.#warn();
|
|
49180
|
+
return { value, backend: "file" };
|
|
49181
|
+
}
|
|
49182
|
+
async delete() {
|
|
49183
|
+
const entry = await this.#entry();
|
|
49184
|
+
let keyringDeleteFailed = false;
|
|
49185
|
+
if (entry) {
|
|
49186
|
+
try {
|
|
49187
|
+
const deleted = await entry.deleteCredential();
|
|
49188
|
+
if (!deleted && await entry.getSecret() != null) {
|
|
49189
|
+
keyringDeleteFailed = true;
|
|
49190
|
+
}
|
|
49191
|
+
} catch {
|
|
49192
|
+
keyringDeleteFailed = true;
|
|
49193
|
+
}
|
|
49194
|
+
}
|
|
49195
|
+
await rm(this.#options.file, { force: true });
|
|
49196
|
+
if (keyringDeleteFailed) {
|
|
49197
|
+
throw new Error(
|
|
49198
|
+
"The operating-system credential store could not remove the Glossa credential."
|
|
49199
|
+
);
|
|
49200
|
+
}
|
|
49201
|
+
}
|
|
49202
|
+
async #entry() {
|
|
49203
|
+
if (this.#options.entryProvider) {
|
|
49204
|
+
try {
|
|
49205
|
+
return await this.#options.entryProvider();
|
|
49206
|
+
} catch {
|
|
49207
|
+
return null;
|
|
49208
|
+
}
|
|
49209
|
+
}
|
|
49210
|
+
try {
|
|
49211
|
+
const { AsyncEntry } = await import("@napi-rs/keyring");
|
|
49212
|
+
return new AsyncEntry(KEYRING_SERVICE, this.#options.account);
|
|
49213
|
+
} catch {
|
|
49214
|
+
return null;
|
|
49215
|
+
}
|
|
49216
|
+
}
|
|
49217
|
+
async #readEntry(entry) {
|
|
49218
|
+
try {
|
|
49219
|
+
const secret = await entry.getSecret();
|
|
49220
|
+
if (secret == null) return null;
|
|
49221
|
+
const bytes = secret instanceof Uint8Array ? secret : Uint8Array.from(secret);
|
|
49222
|
+
return this.#options.parse(
|
|
49223
|
+
new TextDecoder("utf-8", { fatal: true }).decode(bytes)
|
|
49224
|
+
);
|
|
49225
|
+
} catch {
|
|
49226
|
+
return null;
|
|
49227
|
+
}
|
|
49228
|
+
}
|
|
49229
|
+
async #writeEntry(entry, serialized) {
|
|
49230
|
+
await entry.setSecret(new TextEncoder().encode(serialized));
|
|
49231
|
+
}
|
|
49232
|
+
#warn() {
|
|
49233
|
+
if (this.#warned) return;
|
|
49234
|
+
this.#warned = true;
|
|
49235
|
+
(this.#options.warn ?? console.warn)(this.#options.warning);
|
|
49236
|
+
}
|
|
49237
|
+
};
|
|
49238
|
+
|
|
49239
|
+
// src/first-run.ts
|
|
49796
49240
|
var CONNECT_HINT_FILE = "connect-hint-shown";
|
|
49797
49241
|
var CONNECT_HINT_URL = "https://glossa.sh/docs/quickstart";
|
|
49798
49242
|
function shouldShowConnectHint(relayOrigin) {
|
|
49799
49243
|
return relayOrigin === DEFAULT_RELAY_ORIGIN;
|
|
49800
49244
|
}
|
|
49801
49245
|
function connectHintStore(directory = configDirectory()) {
|
|
49802
|
-
const file2 =
|
|
49246
|
+
const file2 = path2.join(directory, CONNECT_HINT_FILE);
|
|
49803
49247
|
return {
|
|
49804
49248
|
async exists() {
|
|
49805
49249
|
try {
|
|
@@ -49816,15 +49260,15 @@ function connectHintStore(directory = configDirectory()) {
|
|
|
49816
49260
|
}
|
|
49817
49261
|
};
|
|
49818
49262
|
}
|
|
49819
|
-
async function announceConnectHint(
|
|
49820
|
-
if (await
|
|
49263
|
+
async function announceConnectHint(store2, log) {
|
|
49264
|
+
if (await store2.exists()) return false;
|
|
49821
49265
|
log(`Next: return to ChatGPT and use Glossa. If it is not connected yet, follow ${CONNECT_HINT_URL}.`);
|
|
49822
|
-
await
|
|
49266
|
+
await store2.mark();
|
|
49823
49267
|
return true;
|
|
49824
49268
|
}
|
|
49825
49269
|
|
|
49826
49270
|
// src/device-store.ts
|
|
49827
|
-
import
|
|
49271
|
+
import path3 from "node:path";
|
|
49828
49272
|
var FILE_DEVICE_WARNING = "Warning: the operating-system credential store is unavailable. Glossa is using a mode-0600 device credential file.";
|
|
49829
49273
|
function parseDeviceCredential(value) {
|
|
49830
49274
|
let parsed;
|
|
@@ -49848,36 +49292,36 @@ function parseDeviceCredential(value) {
|
|
|
49848
49292
|
}
|
|
49849
49293
|
return parsed;
|
|
49850
49294
|
}
|
|
49851
|
-
var
|
|
49295
|
+
var store = new SecureStore({
|
|
49852
49296
|
account: "device",
|
|
49853
|
-
file:
|
|
49297
|
+
file: path3.join(configDirectory(), "device.json"),
|
|
49854
49298
|
warning: FILE_DEVICE_WARNING,
|
|
49855
49299
|
parse: parseDeviceCredential
|
|
49856
49300
|
});
|
|
49857
49301
|
async function loadDeviceCredential() {
|
|
49858
|
-
return (await
|
|
49302
|
+
return (await store.load())?.value ?? null;
|
|
49859
49303
|
}
|
|
49860
49304
|
async function saveDeviceCredential(credential) {
|
|
49861
|
-
await
|
|
49305
|
+
await store.save(credential);
|
|
49862
49306
|
}
|
|
49863
49307
|
async function deleteDeviceCredential() {
|
|
49864
|
-
await
|
|
49308
|
+
await store.delete();
|
|
49865
49309
|
}
|
|
49866
49310
|
|
|
49867
49311
|
// src/device-pairing-lock.ts
|
|
49868
49312
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
49869
49313
|
import { mkdir as mkdir4, readFile as readFile3, rm as rm3, stat, writeFile as writeFile4 } from "node:fs/promises";
|
|
49870
|
-
import
|
|
49871
|
-
import { setTimeout as
|
|
49314
|
+
import path5 from "node:path";
|
|
49315
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
49872
49316
|
|
|
49873
49317
|
// src/update-lock.ts
|
|
49874
49318
|
import { randomUUID } from "node:crypto";
|
|
49875
49319
|
import { mkdir as mkdir3, readdir, rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
|
|
49876
|
-
import
|
|
49320
|
+
import path4 from "node:path";
|
|
49877
49321
|
var UPDATE_SUFFIX = ".update";
|
|
49878
49322
|
var SESSION_SUFFIX = ".session";
|
|
49879
49323
|
function updateRuntimeDirectory() {
|
|
49880
|
-
return
|
|
49324
|
+
return path4.join(configDirectory(), "runtime");
|
|
49881
49325
|
}
|
|
49882
49326
|
function processIsAlive(pid) {
|
|
49883
49327
|
if (!Number.isSafeInteger(pid) || pid <= 0) return false;
|
|
@@ -49907,7 +49351,7 @@ async function activeLeaseFiles(directory, suffix) {
|
|
|
49907
49351
|
for (const entry of entries) {
|
|
49908
49352
|
if (!entry.isFile() || !entry.name.endsWith(suffix)) continue;
|
|
49909
49353
|
const pid = leasePid(entry.name, suffix);
|
|
49910
|
-
const file2 =
|
|
49354
|
+
const file2 = path4.join(directory, entry.name);
|
|
49911
49355
|
if (pid !== null && processIsAlive(pid)) active.push(file2);
|
|
49912
49356
|
else await rm2(file2, { force: true });
|
|
49913
49357
|
}
|
|
@@ -49921,7 +49365,7 @@ async function activeSessionFiles(directory) {
|
|
|
49921
49365
|
}
|
|
49922
49366
|
async function createUpdateLock(directory) {
|
|
49923
49367
|
await mkdir3(directory, { recursive: true, mode: 448 });
|
|
49924
|
-
const lockFile =
|
|
49368
|
+
const lockFile = path4.join(
|
|
49925
49369
|
directory,
|
|
49926
49370
|
`${process.pid}-${randomUUID()}${UPDATE_SUFFIX}`
|
|
49927
49371
|
);
|
|
@@ -49959,7 +49403,7 @@ async function withWorkspaceLease(action, directory = updateRuntimeDirectory())
|
|
|
49959
49403
|
if (await updateLockIsActive(directory)) {
|
|
49960
49404
|
throw new Error("Glossa is updating. Run this workspace again after the update finishes.");
|
|
49961
49405
|
}
|
|
49962
|
-
const leaseFile =
|
|
49406
|
+
const leaseFile = path4.join(
|
|
49963
49407
|
directory,
|
|
49964
49408
|
`${process.pid}-${randomUUID()}${SESSION_SUFFIX}`
|
|
49965
49409
|
);
|
|
@@ -49986,10 +49430,10 @@ var DEVICE_PAIRING_LOCK_POLL_MS = 100;
|
|
|
49986
49430
|
var DEVICE_PAIRING_LOCK_MAX_AGE_MS = 6 * 6e4;
|
|
49987
49431
|
var DEVICE_PAIRING_GUARD_MAX_AGE_MS = 3e4;
|
|
49988
49432
|
function lockPath(directory) {
|
|
49989
|
-
return
|
|
49433
|
+
return path5.join(directory, DEVICE_PAIRING_LOCK);
|
|
49990
49434
|
}
|
|
49991
49435
|
function guardPath(directory) {
|
|
49992
|
-
return
|
|
49436
|
+
return path5.join(directory, DEVICE_PAIRING_LOCK_GUARD);
|
|
49993
49437
|
}
|
|
49994
49438
|
function newOwner() {
|
|
49995
49439
|
return {
|
|
@@ -50044,7 +49488,7 @@ async function acquireGuard(directory, signal) {
|
|
|
50044
49488
|
await rm3(file2, { force: true });
|
|
50045
49489
|
continue;
|
|
50046
49490
|
}
|
|
50047
|
-
await
|
|
49491
|
+
await delay(
|
|
50048
49492
|
DEVICE_PAIRING_LOCK_POLL_MS,
|
|
50049
49493
|
void 0,
|
|
50050
49494
|
signal ? { signal } : void 0
|
|
@@ -50092,7 +49536,7 @@ async function acquireDevicePairingLock(directory, signal) {
|
|
|
50092
49536
|
signal
|
|
50093
49537
|
);
|
|
50094
49538
|
if (reclaimed) continue;
|
|
50095
|
-
await
|
|
49539
|
+
await delay(
|
|
50096
49540
|
DEVICE_PAIRING_LOCK_POLL_MS,
|
|
50097
49541
|
void 0,
|
|
50098
49542
|
signal ? { signal } : void 0
|
|
@@ -50109,53 +49553,72 @@ async function withDevicePairingLease(action, signal, directory = updateRuntimeD
|
|
|
50109
49553
|
}
|
|
50110
49554
|
|
|
50111
49555
|
// src/device-pairing.ts
|
|
50112
|
-
import { setTimeout as
|
|
50113
|
-
|
|
50114
|
-
|
|
50115
|
-
|
|
50116
|
-
function canceledError2() {
|
|
49556
|
+
import { setTimeout as delay2 } from "node:timers/promises";
|
|
49557
|
+
var MAX_PAIRING_CODES = 3;
|
|
49558
|
+
var POLL_INTERVAL_MS = 2e3;
|
|
49559
|
+
function canceledError() {
|
|
50117
49560
|
return new Error("Pairing canceled.");
|
|
50118
49561
|
}
|
|
49562
|
+
async function defaultDelay(milliseconds, signal) {
|
|
49563
|
+
await delay2(milliseconds, void 0, signal ? { signal } : void 0);
|
|
49564
|
+
}
|
|
50119
49565
|
async function pairDevice(endpoints, signal, dependencies = {}) {
|
|
50120
|
-
const
|
|
50121
|
-
const
|
|
49566
|
+
const create3 = dependencies.createPairing ?? createPairing;
|
|
49567
|
+
const redeem = dependencies.redeemPairing ?? redeemPairing;
|
|
50122
49568
|
const name = dependencies.defaultDeviceName ?? defaultDeviceName;
|
|
50123
|
-
const wait = dependencies.
|
|
49569
|
+
const wait = dependencies.delay ?? defaultDelay;
|
|
50124
49570
|
const now = dependencies.now ?? Date.now;
|
|
50125
49571
|
const log = dependencies.log ?? console.log;
|
|
50126
49572
|
const baseFetch = dependencies.fetch ?? fetch;
|
|
50127
49573
|
const fetchRequest = signal ? async (input, init) => await baseFetch(input, { ...init, signal }) : baseFetch;
|
|
50128
49574
|
try {
|
|
50129
49575
|
signal?.throwIfAborted();
|
|
50130
|
-
const challenge = await begin(endpoints, name(), fetchRequest);
|
|
50131
49576
|
log("This computer needs to be paired with Glossa.");
|
|
50132
|
-
|
|
50133
|
-
|
|
50134
|
-
|
|
50135
|
-
const expiresAt = Date.parse(challenge.expiresAt);
|
|
50136
|
-
while (now() < expiresAt) {
|
|
49577
|
+
const deviceName = name();
|
|
49578
|
+
const platform2 = `${process.platform}-${process.arch}`;
|
|
49579
|
+
for (let attempt = 0; attempt < MAX_PAIRING_CODES; attempt += 1) {
|
|
50137
49580
|
signal?.throwIfAborted();
|
|
50138
|
-
await
|
|
50139
|
-
|
|
50140
|
-
|
|
50141
|
-
|
|
50142
|
-
log(`
|
|
50143
|
-
|
|
49581
|
+
const pairing = await create3(endpoints, deviceName, platform2, fetchRequest);
|
|
49582
|
+
log("");
|
|
49583
|
+
log(`Pairing code: ${pairing.code} (valid for 10 minutes)`);
|
|
49584
|
+
log("");
|
|
49585
|
+
log(`Enter it at ${endpoints.relayOrigin}/panel`);
|
|
49586
|
+
const expiresAt = Date.parse(pairing.expiresAt);
|
|
49587
|
+
while (now() < expiresAt) {
|
|
49588
|
+
signal?.throwIfAborted();
|
|
49589
|
+
await wait(POLL_INTERVAL_MS, signal);
|
|
49590
|
+
signal?.throwIfAborted();
|
|
49591
|
+
try {
|
|
49592
|
+
const redeemed = await redeem(endpoints, pairing.code, fetchRequest);
|
|
49593
|
+
if (redeemed === "pending") continue;
|
|
49594
|
+
log(`Paired with Glossa as ${redeemed.device.name}.`);
|
|
49595
|
+
return {
|
|
49596
|
+
relayOrigin: endpoints.relayOrigin,
|
|
49597
|
+
deviceId: redeemed.device.id,
|
|
49598
|
+
deviceName: redeemed.device.name,
|
|
49599
|
+
token: redeemed.token
|
|
49600
|
+
};
|
|
49601
|
+
} catch (error46) {
|
|
49602
|
+
if (!(error46 instanceof PairingCodeExpiredError)) throw error46;
|
|
49603
|
+
log(error46.message);
|
|
49604
|
+
break;
|
|
49605
|
+
}
|
|
49606
|
+
}
|
|
50144
49607
|
}
|
|
50145
|
-
throw new Error("
|
|
49608
|
+
throw new Error("Pairing timed out. Run Glossa again to retry.");
|
|
50146
49609
|
} catch (error46) {
|
|
50147
49610
|
if (signal?.aborted || error46 instanceof Error && error46.name === "AbortError") {
|
|
50148
|
-
throw
|
|
49611
|
+
throw canceledError();
|
|
50149
49612
|
}
|
|
50150
49613
|
throw error46;
|
|
50151
49614
|
}
|
|
50152
49615
|
}
|
|
50153
49616
|
|
|
50154
49617
|
// src/worker/command-service.ts
|
|
50155
|
-
import { spawn
|
|
49618
|
+
import { spawn } from "node:child_process";
|
|
50156
49619
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
50157
49620
|
import { StringDecoder } from "node:string_decoder";
|
|
50158
|
-
import { setTimeout as
|
|
49621
|
+
import { setTimeout as delay3 } from "node:timers/promises";
|
|
50159
49622
|
|
|
50160
49623
|
// src/worker/errors.ts
|
|
50161
49624
|
var WorkerError = class extends Error {
|
|
@@ -50270,7 +49733,7 @@ async function waitForChange(record2, afterSequence, waitMs) {
|
|
|
50270
49733
|
try {
|
|
50271
49734
|
await Promise.race([
|
|
50272
49735
|
change,
|
|
50273
|
-
|
|
49736
|
+
delay3(waitMs, void 0, { signal: waitController.signal })
|
|
50274
49737
|
]);
|
|
50275
49738
|
} finally {
|
|
50276
49739
|
record2.changeWaiters.delete(changed);
|
|
@@ -50427,7 +49890,7 @@ async function terminateProcessTree(child) {
|
|
|
50427
49890
|
if (!child.pid || child.exitCode !== null) return;
|
|
50428
49891
|
if (process.platform === "win32") {
|
|
50429
49892
|
await new Promise((resolve) => {
|
|
50430
|
-
const killer =
|
|
49893
|
+
const killer = spawn("taskkill", ["/pid", String(child.pid), "/t", "/f"], {
|
|
50431
49894
|
stdio: "ignore",
|
|
50432
49895
|
windowsHide: true
|
|
50433
49896
|
});
|
|
@@ -50441,7 +49904,7 @@ async function terminateProcessTree(child) {
|
|
|
50441
49904
|
} catch {
|
|
50442
49905
|
child.kill("SIGTERM");
|
|
50443
49906
|
}
|
|
50444
|
-
await
|
|
49907
|
+
await delay3(2e3);
|
|
50445
49908
|
if (child.exitCode === null) {
|
|
50446
49909
|
try {
|
|
50447
49910
|
process.kill(-child.pid, "SIGKILL");
|
|
@@ -50517,7 +49980,7 @@ var CommandService = class {
|
|
|
50517
49980
|
const invocation = options.argv ? { file: options.argv[0], args: options.argv.slice(1) } : shellInvocation(options.shellCommand);
|
|
50518
49981
|
let child;
|
|
50519
49982
|
try {
|
|
50520
|
-
child =
|
|
49983
|
+
child = spawn(invocation.file, invocation.args, {
|
|
50521
49984
|
cwd: cwd2,
|
|
50522
49985
|
env: process.env,
|
|
50523
49986
|
detached: process.platform !== "win32",
|
|
@@ -50606,7 +50069,7 @@ var CommandService = class {
|
|
|
50606
50069
|
try {
|
|
50607
50070
|
await Promise.race([
|
|
50608
50071
|
record2.completion,
|
|
50609
|
-
|
|
50072
|
+
delay3(waitMs, void 0, { signal: waitController.signal })
|
|
50610
50073
|
]);
|
|
50611
50074
|
} finally {
|
|
50612
50075
|
waitController.abort();
|
|
@@ -50632,7 +50095,7 @@ var CommandService = class {
|
|
|
50632
50095
|
try {
|
|
50633
50096
|
await Promise.race([
|
|
50634
50097
|
record2.completion,
|
|
50635
|
-
|
|
50098
|
+
delay3(waitMs, void 0, { signal: waitController.signal })
|
|
50636
50099
|
]);
|
|
50637
50100
|
} finally {
|
|
50638
50101
|
waitController.abort();
|
|
@@ -50749,14 +50212,14 @@ import {
|
|
|
50749
50212
|
stat as stat3,
|
|
50750
50213
|
writeFile as writeFile5
|
|
50751
50214
|
} from "node:fs/promises";
|
|
50752
|
-
import
|
|
50215
|
+
import path7 from "node:path";
|
|
50753
50216
|
import { performance as performance2 } from "node:perf_hooks";
|
|
50754
50217
|
import { StringDecoder as StringDecoder2 } from "node:string_decoder";
|
|
50755
50218
|
|
|
50756
50219
|
// src/worker/path-policy.ts
|
|
50757
50220
|
import { lstat, realpath, stat as stat2 } from "node:fs/promises";
|
|
50758
50221
|
import os5 from "node:os";
|
|
50759
|
-
import
|
|
50222
|
+
import path6 from "node:path";
|
|
50760
50223
|
function samePath(left, right) {
|
|
50761
50224
|
return process.platform === "win32" ? left.toLowerCase() === right.toLowerCase() : left === right;
|
|
50762
50225
|
}
|
|
@@ -50768,15 +50231,15 @@ function accountHomeDirectory() {
|
|
|
50768
50231
|
}
|
|
50769
50232
|
}
|
|
50770
50233
|
function isWithin(root, candidate) {
|
|
50771
|
-
const relative =
|
|
50772
|
-
return relative === "" || !relative.startsWith(`..${
|
|
50234
|
+
const relative = path6.relative(root, candidate);
|
|
50235
|
+
return relative === "" || !relative.startsWith(`..${path6.sep}`) && relative !== ".." && !path6.isAbsolute(relative);
|
|
50773
50236
|
}
|
|
50774
50237
|
function validateRelativePath(value) {
|
|
50775
50238
|
if (value.includes("\0")) {
|
|
50776
50239
|
throw new WorkerError("invalid_path", "Paths cannot contain null bytes.");
|
|
50777
50240
|
}
|
|
50778
50241
|
const explicitNativePosixPath = process.platform !== "win32" && value.startsWith("./");
|
|
50779
|
-
if (
|
|
50242
|
+
if (path6.isAbsolute(value) || path6.posix.isAbsolute(value) || !explicitNativePosixPath && path6.win32.isAbsolute(value)) {
|
|
50780
50243
|
throw new WorkerError("absolute_path", "Absolute paths are not allowed.");
|
|
50781
50244
|
}
|
|
50782
50245
|
const segments = explicitNativePosixPath ? value.split(/\/+/).filter(Boolean) : value.split(/[\\/]+/);
|
|
@@ -50786,7 +50249,7 @@ function validateRelativePath(value) {
|
|
|
50786
50249
|
return value === "" ? "." : value;
|
|
50787
50250
|
}
|
|
50788
50251
|
async function canonicalizeRoot(candidate) {
|
|
50789
|
-
const root = await realpath(
|
|
50252
|
+
const root = await realpath(path6.resolve(candidate)).catch((error46) => {
|
|
50790
50253
|
if (error46.code === "ENOENT") {
|
|
50791
50254
|
throw new WorkerError("root_not_found", "The workspace directory does not exist.");
|
|
50792
50255
|
}
|
|
@@ -50796,10 +50259,10 @@ async function canonicalizeRoot(candidate) {
|
|
|
50796
50259
|
if (!rootStat.isDirectory()) {
|
|
50797
50260
|
throw new WorkerError("root_not_directory", "The exposed root must be a directory.");
|
|
50798
50261
|
}
|
|
50799
|
-
const filesystemRoot =
|
|
50262
|
+
const filesystemRoot = path6.parse(root).root;
|
|
50800
50263
|
const homes = await Promise.all(
|
|
50801
50264
|
[os5.homedir(), accountHomeDirectory()].map(
|
|
50802
|
-
async (home) => await realpath(home).catch(() =>
|
|
50265
|
+
async (home) => await realpath(home).catch(() => path6.resolve(home))
|
|
50803
50266
|
)
|
|
50804
50267
|
);
|
|
50805
50268
|
const isHomeDirectory = homes.some((home) => samePath(root, home));
|
|
@@ -50835,7 +50298,7 @@ var PathPolicy = class _PathPolicy {
|
|
|
50835
50298
|
return canonical;
|
|
50836
50299
|
}
|
|
50837
50300
|
async resolveDiscoveredExisting(candidate) {
|
|
50838
|
-
const lexical =
|
|
50301
|
+
const lexical = path6.resolve(candidate);
|
|
50839
50302
|
if (!isWithin(this.root, lexical)) {
|
|
50840
50303
|
throw new WorkerError("path_escape", "The requested path escapes the exposed root.");
|
|
50841
50304
|
}
|
|
@@ -50853,7 +50316,7 @@ var PathPolicy = class _PathPolicy {
|
|
|
50853
50316
|
}
|
|
50854
50317
|
async resolveWritableFile(relativePath) {
|
|
50855
50318
|
const lexical = this.resolveLexical(relativePath);
|
|
50856
|
-
const parent =
|
|
50319
|
+
const parent = path6.dirname(lexical);
|
|
50857
50320
|
await this.rejectLinkedComponents(parent);
|
|
50858
50321
|
const canonicalParent = await realpath(parent).catch((error46) => {
|
|
50859
50322
|
if (error46.code === "ENOENT") {
|
|
@@ -50884,7 +50347,7 @@ var PathPolicy = class _PathPolicy {
|
|
|
50884
50347
|
if (error46 instanceof WorkerError) throw error46;
|
|
50885
50348
|
if (error46.code !== "ENOENT") throw error46;
|
|
50886
50349
|
}
|
|
50887
|
-
return
|
|
50350
|
+
return path6.join(canonicalParent, path6.basename(lexical));
|
|
50888
50351
|
}
|
|
50889
50352
|
async resolveWritableDirectory(relativePath, recursive) {
|
|
50890
50353
|
const lexical = this.resolveLexical(relativePath);
|
|
@@ -50907,7 +50370,7 @@ var PathPolicy = class _PathPolicy {
|
|
|
50907
50370
|
if (error46.code !== "ENOENT") throw error46;
|
|
50908
50371
|
}
|
|
50909
50372
|
if (!recursive) {
|
|
50910
|
-
const parent =
|
|
50373
|
+
const parent = path6.dirname(lexical);
|
|
50911
50374
|
await this.rejectLinkedComponents(parent);
|
|
50912
50375
|
const canonicalParent = await realpath(parent).catch(
|
|
50913
50376
|
(error46) => {
|
|
@@ -50927,11 +50390,11 @@ var PathPolicy = class _PathPolicy {
|
|
|
50927
50390
|
throw new WorkerError("not_directory", "The destination parent is not a directory.");
|
|
50928
50391
|
}
|
|
50929
50392
|
return {
|
|
50930
|
-
target:
|
|
50393
|
+
target: path6.join(canonicalParent, path6.basename(lexical)),
|
|
50931
50394
|
exists: false
|
|
50932
50395
|
};
|
|
50933
50396
|
}
|
|
50934
|
-
let existingAncestor =
|
|
50397
|
+
let existingAncestor = path6.dirname(lexical);
|
|
50935
50398
|
while (!samePath(existingAncestor, this.root)) {
|
|
50936
50399
|
try {
|
|
50937
50400
|
await this.rejectLinkedComponents(existingAncestor);
|
|
@@ -50943,9 +50406,9 @@ var PathPolicy = class _PathPolicy {
|
|
|
50943
50406
|
throw new WorkerError("not_directory", "The destination parent is not a directory.");
|
|
50944
50407
|
}
|
|
50945
50408
|
return {
|
|
50946
|
-
target:
|
|
50409
|
+
target: path6.join(
|
|
50947
50410
|
canonicalAncestor,
|
|
50948
|
-
|
|
50411
|
+
path6.relative(existingAncestor, lexical)
|
|
50949
50412
|
),
|
|
50950
50413
|
exists: false
|
|
50951
50414
|
};
|
|
@@ -50953,13 +50416,13 @@ var PathPolicy = class _PathPolicy {
|
|
|
50953
50416
|
if (error46 instanceof WorkerError) throw error46;
|
|
50954
50417
|
if (error46.code !== "ENOENT") throw error46;
|
|
50955
50418
|
}
|
|
50956
|
-
existingAncestor =
|
|
50419
|
+
existingAncestor = path6.dirname(existingAncestor);
|
|
50957
50420
|
}
|
|
50958
50421
|
return { target: lexical, exists: false };
|
|
50959
50422
|
}
|
|
50960
50423
|
async resolveVacantPath(relativePath) {
|
|
50961
50424
|
const lexical = this.resolveLexical(relativePath);
|
|
50962
|
-
const parent =
|
|
50425
|
+
const parent = path6.dirname(lexical);
|
|
50963
50426
|
await this.rejectLinkedComponents(parent);
|
|
50964
50427
|
const canonicalParent = await realpath(parent).catch(
|
|
50965
50428
|
(error46) => {
|
|
@@ -50988,11 +50451,11 @@ var PathPolicy = class _PathPolicy {
|
|
|
50988
50451
|
if (error46 instanceof WorkerError) throw error46;
|
|
50989
50452
|
if (error46.code !== "ENOENT") throw error46;
|
|
50990
50453
|
}
|
|
50991
|
-
return
|
|
50454
|
+
return path6.join(canonicalParent, path6.basename(lexical));
|
|
50992
50455
|
}
|
|
50993
50456
|
resolveLexical(relativePath) {
|
|
50994
50457
|
const validated = validateRelativePath(relativePath);
|
|
50995
|
-
const candidate =
|
|
50458
|
+
const candidate = path6.resolve(this.root, validated);
|
|
50996
50459
|
if (!isWithin(this.root, candidate)) {
|
|
50997
50460
|
throw new WorkerError("path_escape", "The requested path escapes the exposed root.");
|
|
50998
50461
|
}
|
|
@@ -51002,11 +50465,11 @@ var PathPolicy = class _PathPolicy {
|
|
|
51002
50465
|
if (!isWithin(this.root, candidate)) {
|
|
51003
50466
|
throw new WorkerError("path_escape", "The requested path escapes the exposed root.");
|
|
51004
50467
|
}
|
|
51005
|
-
const relative =
|
|
50468
|
+
const relative = path6.relative(this.root, candidate);
|
|
51006
50469
|
if (!relative) return;
|
|
51007
50470
|
let current = this.root;
|
|
51008
|
-
for (const segment of relative.split(
|
|
51009
|
-
current =
|
|
50471
|
+
for (const segment of relative.split(path6.sep)) {
|
|
50472
|
+
current = path6.join(current, segment);
|
|
51010
50473
|
try {
|
|
51011
50474
|
const currentStat = await lstat(current);
|
|
51012
50475
|
if (currentStat.isSymbolicLink()) {
|
|
@@ -51029,12 +50492,12 @@ function sha256(content) {
|
|
|
51029
50492
|
return createHash("sha256").update(content).digest("hex");
|
|
51030
50493
|
}
|
|
51031
50494
|
function isPathWithin(root, candidate) {
|
|
51032
|
-
const relative =
|
|
51033
|
-
return relative === "" || !relative.startsWith(`..${
|
|
50495
|
+
const relative = path7.relative(root, candidate);
|
|
50496
|
+
return relative === "" || !relative.startsWith(`..${path7.sep}`) && relative !== ".." && !path7.isAbsolute(relative);
|
|
51034
50497
|
}
|
|
51035
50498
|
var fileWriteTails = /* @__PURE__ */ new Map();
|
|
51036
50499
|
async function withFileWriteLock(target, operation) {
|
|
51037
|
-
const normalized =
|
|
50500
|
+
const normalized = path7.normalize(target);
|
|
51038
50501
|
const key = process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
51039
50502
|
const predecessor = fileWriteTails.get(key);
|
|
51040
50503
|
let release;
|
|
@@ -51051,7 +50514,7 @@ async function withFileWriteLock(target, operation) {
|
|
|
51051
50514
|
}
|
|
51052
50515
|
}
|
|
51053
50516
|
async function withFileWriteLocks(targets, operation) {
|
|
51054
|
-
const unique = [...new Set(targets.map((target) =>
|
|
50517
|
+
const unique = [...new Set(targets.map((target) => path7.normalize(target)))].sort(
|
|
51055
50518
|
(left, right) => left.localeCompare(right)
|
|
51056
50519
|
);
|
|
51057
50520
|
const run = async (index) => {
|
|
@@ -51087,7 +50550,7 @@ function compareNames(left, right) {
|
|
|
51087
50550
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
51088
50551
|
}
|
|
51089
50552
|
function relativeSortKey(root, target) {
|
|
51090
|
-
const relative =
|
|
50553
|
+
const relative = path7.relative(root, target);
|
|
51091
50554
|
if (!relative) return ".";
|
|
51092
50555
|
return process.platform === "win32" ? relative.replaceAll("\\", "/") : relative;
|
|
51093
50556
|
}
|
|
@@ -51471,7 +50934,7 @@ var FileService = class {
|
|
|
51471
50934
|
if (batch.overflow) throw scanLimitError();
|
|
51472
50935
|
scannedEntries += batch.children.length;
|
|
51473
50936
|
for (const child of batch.children) {
|
|
51474
|
-
const target =
|
|
50937
|
+
const target = path7.join(directory, child.name);
|
|
51475
50938
|
heapPush(frontier, {
|
|
51476
50939
|
target,
|
|
51477
50940
|
sortKey: relativeSortKey(this.policy.root, target),
|
|
@@ -51495,7 +50958,7 @@ var FileService = class {
|
|
|
51495
50958
|
throw scanLimitError();
|
|
51496
50959
|
}
|
|
51497
50960
|
scannedEntries += 1;
|
|
51498
|
-
const target =
|
|
50961
|
+
const target = path7.join(directory, child.name);
|
|
51499
50962
|
let childStat;
|
|
51500
50963
|
try {
|
|
51501
50964
|
childStat = await this.#withinDeadline(
|
|
@@ -51647,7 +51110,7 @@ var FileService = class {
|
|
|
51647
51110
|
if (!patterns) return false;
|
|
51648
51111
|
const normalized = relativePath.replaceAll("\\", "/");
|
|
51649
51112
|
try {
|
|
51650
|
-
return patterns.some((pattern) =>
|
|
51113
|
+
return patterns.some((pattern) => path7.posix.matchesGlob(normalized, pattern));
|
|
51651
51114
|
} catch {
|
|
51652
51115
|
throw new WorkerError("invalid_search", "Search glob pattern is invalid.");
|
|
51653
51116
|
}
|
|
@@ -51676,7 +51139,7 @@ var FileService = class {
|
|
|
51676
51139
|
const searchFile = async (target) => {
|
|
51677
51140
|
const relative = displayPath(this.policy.root, target);
|
|
51678
51141
|
if (extensions && !extensions.some(
|
|
51679
|
-
(extension) =>
|
|
51142
|
+
(extension) => path7.basename(target).toLowerCase().endsWith(extension)
|
|
51680
51143
|
)) {
|
|
51681
51144
|
return false;
|
|
51682
51145
|
}
|
|
@@ -51771,7 +51234,7 @@ var FileService = class {
|
|
|
51771
51234
|
scannedEntries += batch.children.length;
|
|
51772
51235
|
for (const child of batch.children) {
|
|
51773
51236
|
this.#assertBeforeDeadline(deadlineAt);
|
|
51774
|
-
const target =
|
|
51237
|
+
const target = path7.join(directory, child.name);
|
|
51775
51238
|
let targetType;
|
|
51776
51239
|
if (this.#trustDirectoryEntryTypes) {
|
|
51777
51240
|
if (child.isSymbolicLink()) {
|
|
@@ -52081,7 +51544,7 @@ var FileService = class {
|
|
|
52081
51544
|
);
|
|
52082
51545
|
}
|
|
52083
51546
|
if (expectedSha256) await requireRevision(target, expectedSha256);
|
|
52084
|
-
const temporary =
|
|
51547
|
+
const temporary = path7.join(path7.dirname(target), `.glossa-${randomUUID4()}.tmp`);
|
|
52085
51548
|
try {
|
|
52086
51549
|
await writeFile5(temporary, bytes, { flag: "wx", mode: 384 });
|
|
52087
51550
|
target = await this.policy.resolveWritableFile(relativePath);
|
|
@@ -52329,18 +51792,25 @@ var DEFAULT_HEARTBEAT_MS = 15e3;
|
|
|
52329
51792
|
var CAPACITY_REFRESH_POLL_MS = 1;
|
|
52330
51793
|
var MAX_CONCURRENT_JOBS = 5;
|
|
52331
51794
|
var WORKER_TOKEN_PATTERN = /^glw_[A-Za-z0-9_-]{43}$/;
|
|
51795
|
+
var WORKER_CAPABILITIES = {
|
|
51796
|
+
commandProgress: true,
|
|
51797
|
+
concurrentJobs: true,
|
|
51798
|
+
structuredReads: true,
|
|
51799
|
+
structuredMutations: true,
|
|
51800
|
+
commandOutputRanges: true
|
|
51801
|
+
};
|
|
52332
51802
|
var DeviceRejectedError = class extends Error {
|
|
52333
51803
|
constructor() {
|
|
52334
51804
|
super("The relay rejected the device credential.");
|
|
52335
51805
|
this.name = "DeviceRejectedError";
|
|
52336
51806
|
}
|
|
52337
51807
|
};
|
|
52338
|
-
var
|
|
52339
|
-
constructor(
|
|
51808
|
+
var RelayProtocolUnsupportedError = class extends Error {
|
|
51809
|
+
constructor() {
|
|
52340
51810
|
super(
|
|
52341
|
-
|
|
51811
|
+
"The relay does not support this Glossa worker protocol. Update the relay before reconnecting this workspace."
|
|
52342
51812
|
);
|
|
52343
|
-
this.name = "
|
|
51813
|
+
this.name = "RelayProtocolUnsupportedError";
|
|
52344
51814
|
}
|
|
52345
51815
|
};
|
|
52346
51816
|
var RelayResponseError = class extends Error {
|
|
@@ -52351,19 +51821,20 @@ var RelayResponseError = class extends Error {
|
|
|
52351
51821
|
}
|
|
52352
51822
|
status;
|
|
52353
51823
|
};
|
|
52354
|
-
function
|
|
52355
|
-
if (value === void 0) return void 0;
|
|
51824
|
+
function requiredWorkerToken(value) {
|
|
52356
51825
|
if (typeof value !== "string" || !WORKER_TOKEN_PATTERN.test(value)) {
|
|
52357
51826
|
throw new Error("The relay returned an invalid worker credential.");
|
|
52358
51827
|
}
|
|
52359
51828
|
return value;
|
|
52360
51829
|
}
|
|
52361
|
-
function
|
|
51830
|
+
function acceptsCurrentCapabilities(value) {
|
|
52362
51831
|
if (typeof value !== "object" || value === null) return false;
|
|
52363
51832
|
if (!("capabilities" in value)) return false;
|
|
52364
51833
|
const capabilities = value.capabilities;
|
|
52365
51834
|
if (typeof capabilities !== "object" || capabilities === null) return false;
|
|
52366
|
-
return
|
|
51835
|
+
return Object.keys(WORKER_CAPABILITIES).every(
|
|
51836
|
+
(capability) => capabilities[capability] === true
|
|
51837
|
+
);
|
|
52367
51838
|
}
|
|
52368
51839
|
function jobLane(job) {
|
|
52369
51840
|
switch (job.type) {
|
|
@@ -52386,25 +51857,25 @@ function jobLane(job) {
|
|
|
52386
51857
|
return "mutation";
|
|
52387
51858
|
}
|
|
52388
51859
|
}
|
|
52389
|
-
function acceptedJobTypes(counts, total
|
|
51860
|
+
function acceptedJobTypes(counts, total) {
|
|
52390
51861
|
if (total >= MAX_CONCURRENT_JOBS) return [];
|
|
52391
51862
|
const accepted = [];
|
|
52392
51863
|
if (counts.status < 1) {
|
|
52393
|
-
accepted.push("get_command");
|
|
52394
|
-
if (commandOutputRanges) accepted.push("read_command_output");
|
|
51864
|
+
accepted.push("get_command", "read_command_output");
|
|
52395
51865
|
}
|
|
52396
51866
|
if (counts.cancel < 1) accepted.push("cancel_command");
|
|
52397
51867
|
if (counts.read < 2) {
|
|
52398
|
-
accepted.push("read_file");
|
|
52399
|
-
if (structuredReads) {
|
|
52400
|
-
accepted.push("list_files", "search_text", "read_file_range");
|
|
52401
|
-
}
|
|
51868
|
+
accepted.push("read_file", "list_files", "search_text", "read_file_range");
|
|
52402
51869
|
}
|
|
52403
51870
|
if (counts.mutation < 1) {
|
|
52404
|
-
accepted.push(
|
|
52405
|
-
|
|
52406
|
-
|
|
52407
|
-
|
|
51871
|
+
accepted.push(
|
|
51872
|
+
"write_file",
|
|
51873
|
+
"edit_file",
|
|
51874
|
+
"make_directory",
|
|
51875
|
+
"delete_path",
|
|
51876
|
+
"move_path",
|
|
51877
|
+
"run_command"
|
|
51878
|
+
);
|
|
52408
51879
|
}
|
|
52409
51880
|
return accepted;
|
|
52410
51881
|
}
|
|
@@ -52446,7 +51917,7 @@ var RemoteWorker = class {
|
|
|
52446
51917
|
constructor(options) {
|
|
52447
51918
|
this.#origin = new URL(options.origin);
|
|
52448
51919
|
this.#deviceToken = options.deviceToken;
|
|
52449
|
-
this.#accessProfile = options.accessProfile;
|
|
51920
|
+
this.#accessProfile = options.accessProfile ?? "workspace";
|
|
52450
51921
|
this.#workspaceLabel = options.workspaceLabel;
|
|
52451
51922
|
this.#workerVersion = options.workerVersion;
|
|
52452
51923
|
this.#worker = options.worker;
|
|
@@ -52472,20 +51943,17 @@ var RemoteWorker = class {
|
|
|
52472
51943
|
registeredSession = session;
|
|
52473
51944
|
this.#onStatus({
|
|
52474
51945
|
state: "connected",
|
|
52475
|
-
reconnected: connectedBefore
|
|
52476
|
-
legacyRelay: session.legacyRelay,
|
|
52477
|
-
accessProfileAccepted: session.accessProfileAccepted,
|
|
52478
|
-
workspaceLabelAccepted: session.workspaceLabelAccepted
|
|
51946
|
+
reconnected: connectedBefore
|
|
52479
51947
|
});
|
|
52480
51948
|
connectedBefore = true;
|
|
52481
51949
|
failures = 0;
|
|
52482
51950
|
await this.#pollGeneration(session);
|
|
52483
51951
|
} catch (error46) {
|
|
52484
51952
|
if (this.#signal.aborted) return;
|
|
52485
|
-
if (error46 instanceof DeviceRejectedError || error46 instanceof
|
|
51953
|
+
if (error46 instanceof DeviceRejectedError || error46 instanceof RelayProtocolUnsupportedError) {
|
|
52486
51954
|
throw error46;
|
|
52487
51955
|
}
|
|
52488
|
-
const
|
|
51956
|
+
const delay5 = reconnectDelayMs(
|
|
52489
51957
|
failures,
|
|
52490
51958
|
this.#random,
|
|
52491
51959
|
this.#reconnectBaseMs,
|
|
@@ -52495,10 +51963,10 @@ var RemoteWorker = class {
|
|
|
52495
51963
|
this.#onStatus({
|
|
52496
51964
|
state: "retrying",
|
|
52497
51965
|
error: error46 instanceof Error ? error46 : new Error(String(error46)),
|
|
52498
|
-
retryInMs:
|
|
51966
|
+
retryInMs: delay5
|
|
52499
51967
|
});
|
|
52500
51968
|
try {
|
|
52501
|
-
await this.#sleep(
|
|
51969
|
+
await this.#sleep(delay5, this.#signal);
|
|
52502
51970
|
} catch (sleepError) {
|
|
52503
51971
|
if (this.#signal.aborted) return;
|
|
52504
51972
|
throw sleepError;
|
|
@@ -52511,153 +51979,33 @@ var RemoteWorker = class {
|
|
|
52511
51979
|
}
|
|
52512
51980
|
}
|
|
52513
51981
|
async #register() {
|
|
52514
|
-
|
|
52515
|
-
|
|
52516
|
-
|
|
52517
|
-
commandProgress: true,
|
|
52518
|
-
concurrentJobs: true,
|
|
52519
|
-
structuredReads: true,
|
|
52520
|
-
structuredMutations: true,
|
|
52521
|
-
commandOutputRanges: true
|
|
52522
|
-
}
|
|
52523
|
-
};
|
|
52524
|
-
const mutationBody = {
|
|
52525
|
-
workerId: this.#workerId,
|
|
52526
|
-
capabilities: {
|
|
52527
|
-
commandProgress: true,
|
|
52528
|
-
concurrentJobs: true,
|
|
52529
|
-
structuredReads: true,
|
|
52530
|
-
structuredMutations: true
|
|
52531
|
-
}
|
|
52532
|
-
};
|
|
52533
|
-
const structuredBody = {
|
|
52534
|
-
workerId: this.#workerId,
|
|
52535
|
-
capabilities: {
|
|
52536
|
-
commandProgress: true,
|
|
52537
|
-
concurrentJobs: true,
|
|
52538
|
-
structuredReads: true
|
|
52539
|
-
}
|
|
52540
|
-
};
|
|
52541
|
-
const concurrentBody = {
|
|
52542
|
-
workerId: this.#workerId,
|
|
52543
|
-
capabilities: { commandProgress: true, concurrentJobs: true }
|
|
52544
|
-
};
|
|
52545
|
-
const capabilityBodies = [
|
|
52546
|
-
currentBody,
|
|
52547
|
-
mutationBody,
|
|
52548
|
-
structuredBody,
|
|
52549
|
-
concurrentBody,
|
|
52550
|
-
{
|
|
51982
|
+
let response;
|
|
51983
|
+
try {
|
|
51984
|
+
response = await this.#post("/device/register", {
|
|
52551
51985
|
workerId: this.#workerId,
|
|
52552
|
-
|
|
52553
|
-
|
|
52554
|
-
|
|
52555
|
-
|
|
52556
|
-
|
|
52557
|
-
|
|
52558
|
-
|
|
52559
|
-
|
|
52560
|
-
},
|
|
52561
|
-
...this.#workspaceLabel ? [{
|
|
52562
|
-
includeVersion: Boolean(this.#workerVersion),
|
|
52563
|
-
includeLabel: false
|
|
52564
|
-
}] : [],
|
|
52565
|
-
...this.#workerVersion && this.#workspaceLabel ? [{ includeVersion: false, includeLabel: true }] : [],
|
|
52566
|
-
{ includeVersion: false, includeLabel: false }
|
|
52567
|
-
];
|
|
52568
|
-
const attempts = [];
|
|
52569
|
-
const seenAttempts = /* @__PURE__ */ new Set();
|
|
52570
|
-
const pushAttempt = (base, includeProfile, includeVersion, includeLabel, legacyRelay = false) => {
|
|
52571
|
-
const body = {
|
|
52572
|
-
...base,
|
|
52573
|
-
...includeProfile && this.#accessProfile ? { accessProfile: this.#accessProfile } : {},
|
|
52574
|
-
...includeVersion && this.#workerVersion ? { workerVersion: this.#workerVersion } : {},
|
|
52575
|
-
...includeLabel && this.#workspaceLabel ? { workspaceLabel: this.#workspaceLabel } : {}
|
|
52576
|
-
};
|
|
52577
|
-
const key = JSON.stringify(body);
|
|
52578
|
-
if (seenAttempts.has(key)) return;
|
|
52579
|
-
seenAttempts.add(key);
|
|
52580
|
-
attempts.push({ body, legacyRelay });
|
|
52581
|
-
};
|
|
52582
|
-
if (this.#accessProfile) {
|
|
52583
|
-
for (const metadata of metadataPhases) {
|
|
52584
|
-
for (const body of capabilityBodies) {
|
|
52585
|
-
pushAttempt(
|
|
52586
|
-
body,
|
|
52587
|
-
true,
|
|
52588
|
-
metadata.includeVersion,
|
|
52589
|
-
metadata.includeLabel
|
|
52590
|
-
);
|
|
52591
|
-
}
|
|
52592
|
-
}
|
|
52593
|
-
}
|
|
52594
|
-
if (this.#accessProfile === void 0 || this.#accessProfile === "system") {
|
|
52595
|
-
for (const metadata of metadataPhases) {
|
|
52596
|
-
for (const body of capabilityBodies) {
|
|
52597
|
-
pushAttempt(
|
|
52598
|
-
body,
|
|
52599
|
-
false,
|
|
52600
|
-
metadata.includeVersion,
|
|
52601
|
-
metadata.includeLabel
|
|
52602
|
-
);
|
|
52603
|
-
}
|
|
52604
|
-
}
|
|
52605
|
-
attempts.push({ body: {}, legacyRelay: true });
|
|
52606
|
-
}
|
|
52607
|
-
for (const attempt of attempts) {
|
|
52608
|
-
let response;
|
|
52609
|
-
try {
|
|
52610
|
-
response = await this.#post("/device/register", attempt.body);
|
|
52611
|
-
} catch (error46) {
|
|
52612
|
-
if (error46 instanceof RelayResponseError && error46.status === 400) {
|
|
52613
|
-
continue;
|
|
52614
|
-
}
|
|
52615
|
-
throw error46;
|
|
52616
|
-
}
|
|
52617
|
-
const value = await response.json();
|
|
52618
|
-
if (typeof value !== "object" || value === null || !("generation" in value) || typeof value.generation !== "string") {
|
|
52619
|
-
throw new Error("The relay returned an invalid registration response.");
|
|
52620
|
-
}
|
|
52621
|
-
if (!attempt.legacyRelay && (!("workerId" in value) || value.workerId !== this.#workerId)) {
|
|
52622
|
-
throw new Error("The relay returned an invalid registration response.");
|
|
52623
|
-
}
|
|
52624
|
-
const workerToken = "workerToken" in value ? optionalWorkerToken(value.workerToken) : void 0;
|
|
52625
|
-
const accessProfileAccepted = this.#accessProfile === void 0 || this.#accessProfile === "system" || "accessProfile" in value && value.accessProfile === this.#accessProfile;
|
|
52626
|
-
const session = {
|
|
52627
|
-
generation: value.generation,
|
|
52628
|
-
legacyRelay: attempt.legacyRelay,
|
|
52629
|
-
concurrentJobs: !attempt.legacyRelay && supportsCapability(value, "concurrentJobs"),
|
|
52630
|
-
structuredReads: !attempt.legacyRelay && supportsCapability(value, "structuredReads"),
|
|
52631
|
-
structuredMutations: !attempt.legacyRelay && supportsCapability(value, "structuredMutations"),
|
|
52632
|
-
commandOutputRanges: !attempt.legacyRelay && supportsCapability(value, "commandOutputRanges"),
|
|
52633
|
-
accessProfileAccepted,
|
|
52634
|
-
workspaceLabelAccepted: this.#workspaceLabel === void 0 || "workspaceLabel" in value && value.workspaceLabel === this.#workspaceLabel,
|
|
52635
|
-
...workerToken ? { workerToken } : {}
|
|
52636
|
-
};
|
|
52637
|
-
if (!accessProfileAccepted && this.#accessProfile) {
|
|
52638
|
-
throw new RelayAccessProfileUnsupportedError(this.#accessProfile);
|
|
51986
|
+
accessProfile: this.#accessProfile,
|
|
51987
|
+
...this.#workerVersion ? { workerVersion: this.#workerVersion } : {},
|
|
51988
|
+
...this.#workspaceLabel ? { workspaceLabel: this.#workspaceLabel } : {},
|
|
51989
|
+
capabilities: WORKER_CAPABILITIES
|
|
51990
|
+
});
|
|
51991
|
+
} catch (error46) {
|
|
51992
|
+
if (error46 instanceof RelayResponseError && error46.status === 400) {
|
|
51993
|
+
throw new RelayProtocolUnsupportedError();
|
|
52639
51994
|
}
|
|
52640
|
-
|
|
51995
|
+
throw error46;
|
|
52641
51996
|
}
|
|
52642
|
-
|
|
52643
|
-
|
|
51997
|
+
const value = await response.json();
|
|
51998
|
+
if (typeof value !== "object" || value === null || !("generation" in value) || typeof value.generation !== "string" || !("workerId" in value) || value.workerId !== this.#workerId || !("workerToken" in value) || !("accessProfile" in value) || value.accessProfile !== this.#accessProfile || !acceptsCurrentCapabilities(value) || this.#workspaceLabel !== void 0 && (!("workspaceLabel" in value) || value.workspaceLabel !== this.#workspaceLabel)) {
|
|
51999
|
+
throw new RelayProtocolUnsupportedError();
|
|
52644
52000
|
}
|
|
52645
|
-
|
|
52001
|
+
return {
|
|
52002
|
+
generation: value.generation,
|
|
52003
|
+
workerToken: requiredWorkerToken(value.workerToken)
|
|
52004
|
+
};
|
|
52646
52005
|
}
|
|
52647
52006
|
async #pollGeneration(session) {
|
|
52648
|
-
if (session.legacyRelay || !session.concurrentJobs) {
|
|
52649
|
-
await this.#pollSequentially(session);
|
|
52650
|
-
return;
|
|
52651
|
-
}
|
|
52652
52007
|
await this.#pollConcurrently(session);
|
|
52653
52008
|
}
|
|
52654
|
-
async #pollSequentially(session) {
|
|
52655
|
-
while (!this.#signal.aborted) {
|
|
52656
|
-
const job = await this.#pollForJob(session);
|
|
52657
|
-
if (!job) continue;
|
|
52658
|
-
await this.#handleAndPost(session, job, true);
|
|
52659
|
-
}
|
|
52660
|
-
}
|
|
52661
52009
|
async #pollConcurrently(session) {
|
|
52662
52010
|
const counts = {
|
|
52663
52011
|
status: 0,
|
|
@@ -52715,7 +52063,7 @@ var RemoteWorker = class {
|
|
|
52715
52063
|
counts[lane] += 1;
|
|
52716
52064
|
ensureHeartbeat();
|
|
52717
52065
|
let task;
|
|
52718
|
-
task = this.#handleAndPost(session, job
|
|
52066
|
+
task = this.#handleAndPost(session, job).catch((error46) => {
|
|
52719
52067
|
failure ??= error46;
|
|
52720
52068
|
}).finally(() => {
|
|
52721
52069
|
counts[lane] -= 1;
|
|
@@ -52728,13 +52076,7 @@ var RemoteWorker = class {
|
|
|
52728
52076
|
try {
|
|
52729
52077
|
while (!this.#signal.aborted) {
|
|
52730
52078
|
if (failure !== void 0) throw failure;
|
|
52731
|
-
const acceptedTypes = acceptedJobTypes(
|
|
52732
|
-
counts,
|
|
52733
|
-
inFlight.size,
|
|
52734
|
-
session.structuredReads,
|
|
52735
|
-
session.structuredMutations,
|
|
52736
|
-
session.commandOutputRanges
|
|
52737
|
-
);
|
|
52079
|
+
const acceptedTypes = acceptedJobTypes(counts, inFlight.size);
|
|
52738
52080
|
if (acceptedTypes.length === 0) {
|
|
52739
52081
|
await Promise.race(inFlight);
|
|
52740
52082
|
continue;
|
|
@@ -52760,13 +52102,7 @@ var RemoteWorker = class {
|
|
|
52760
52102
|
}
|
|
52761
52103
|
if (failure !== void 0) throw failure;
|
|
52762
52104
|
observedCapacityVersion = capacityVersion;
|
|
52763
|
-
const refreshedTypes = acceptedJobTypes(
|
|
52764
|
-
counts,
|
|
52765
|
-
inFlight.size,
|
|
52766
|
-
session.structuredReads,
|
|
52767
|
-
session.structuredMutations,
|
|
52768
|
-
session.commandOutputRanges
|
|
52769
|
-
);
|
|
52105
|
+
const refreshedTypes = acceptedJobTypes(counts, inFlight.size);
|
|
52770
52106
|
const newlyAcceptedTypes = refreshedTypes.filter(
|
|
52771
52107
|
(type) => !acceptedTypes.includes(type)
|
|
52772
52108
|
);
|
|
@@ -52805,7 +52141,7 @@ var RemoteWorker = class {
|
|
|
52805
52141
|
async #pollForJob(session, acceptedTypes, waitMs) {
|
|
52806
52142
|
const response = await this.#post(
|
|
52807
52143
|
"/device/poll",
|
|
52808
|
-
|
|
52144
|
+
{
|
|
52809
52145
|
workerId: this.#workerId,
|
|
52810
52146
|
generation: session.generation,
|
|
52811
52147
|
...acceptedTypes ? { acceptedTypes } : {},
|
|
@@ -52823,26 +52159,12 @@ var RemoteWorker = class {
|
|
|
52823
52159
|
}
|
|
52824
52160
|
return parsed.data;
|
|
52825
52161
|
}
|
|
52826
|
-
async #handleAndPost(session, job
|
|
52827
|
-
const
|
|
52828
|
-
void this.#post(
|
|
52829
|
-
"/device/heartbeat",
|
|
52830
|
-
{ workerId: this.#workerId, generation: session.generation },
|
|
52831
|
-
session.workerToken
|
|
52832
|
-
).catch(() => {
|
|
52833
|
-
});
|
|
52834
|
-
}, this.#heartbeatMs) : void 0;
|
|
52835
|
-
heartbeat?.unref();
|
|
52836
|
-
let result;
|
|
52837
|
-
try {
|
|
52838
|
-
result = await this.#worker.handle(job);
|
|
52839
|
-
} finally {
|
|
52840
|
-
if (heartbeat) clearInterval(heartbeat);
|
|
52841
|
-
}
|
|
52162
|
+
async #handleAndPost(session, job) {
|
|
52163
|
+
const result = await this.#worker.handle(job);
|
|
52842
52164
|
try {
|
|
52843
52165
|
await this.#post(
|
|
52844
52166
|
"/device/result",
|
|
52845
|
-
|
|
52167
|
+
{ workerId: this.#workerId, result },
|
|
52846
52168
|
session.workerToken
|
|
52847
52169
|
);
|
|
52848
52170
|
} catch (error46) {
|
|
@@ -52851,39 +52173,24 @@ var RemoteWorker = class {
|
|
|
52851
52173
|
}
|
|
52852
52174
|
}
|
|
52853
52175
|
async #unregister(session) {
|
|
52854
|
-
|
|
52855
|
-
|
|
52856
|
-
|
|
52857
|
-
|
|
52858
|
-
|
|
52859
|
-
|
|
52860
|
-
|
|
52861
|
-
|
|
52862
|
-
|
|
52863
|
-
|
|
52864
|
-
|
|
52865
|
-
|
|
52866
|
-
}
|
|
52867
|
-
);
|
|
52868
|
-
if (response.ok) return "accepted";
|
|
52869
|
-
return [400, 401, 404, 409].includes(response.status) ? "rejected" : "unreachable";
|
|
52870
|
-
} catch {
|
|
52871
|
-
return "unreachable";
|
|
52872
|
-
}
|
|
52873
|
-
};
|
|
52874
|
-
if (!session?.workerToken) {
|
|
52875
|
-
await unregister(`Device ${this.#deviceToken}`);
|
|
52876
|
-
return;
|
|
52877
|
-
}
|
|
52878
|
-
const result = await unregister(`Worker ${session.workerToken}`);
|
|
52879
|
-
if (result === "rejected") {
|
|
52880
|
-
await unregister(`Device ${this.#deviceToken}`);
|
|
52176
|
+
if (!session) return;
|
|
52177
|
+
try {
|
|
52178
|
+
await this.#fetcher(new URL("/device/unregister", this.#origin), {
|
|
52179
|
+
method: "POST",
|
|
52180
|
+
headers: {
|
|
52181
|
+
authorization: `Worker ${session.workerToken}`,
|
|
52182
|
+
"content-type": "application/json"
|
|
52183
|
+
},
|
|
52184
|
+
body: JSON.stringify({ workerId: this.#workerId }),
|
|
52185
|
+
signal: AbortSignal.timeout(3e3)
|
|
52186
|
+
});
|
|
52187
|
+
} catch {
|
|
52881
52188
|
}
|
|
52882
52189
|
}
|
|
52883
|
-
async #post(
|
|
52190
|
+
async #post(path12, body, workerToken) {
|
|
52884
52191
|
const timeout = AbortSignal.timeout(WORKER_REQUEST_TIMEOUT_MS);
|
|
52885
52192
|
const signal = AbortSignal.any([this.#signal, timeout]);
|
|
52886
|
-
const response = await this.#fetcher(new URL(
|
|
52193
|
+
const response = await this.#fetcher(new URL(path12, this.#origin), {
|
|
52887
52194
|
method: "POST",
|
|
52888
52195
|
headers: {
|
|
52889
52196
|
authorization: workerToken ? `Worker ${workerToken}` : `Device ${this.#deviceToken}`,
|
|
@@ -53040,27 +52347,6 @@ function accessProfileSummary(accessProfile) {
|
|
|
53040
52347
|
return "System access: clients can modify files and run commands with this account's full environment, permissions, credentials, and network access.";
|
|
53041
52348
|
}
|
|
53042
52349
|
}
|
|
53043
|
-
function accessProfileNotice(status, requestedProfile) {
|
|
53044
|
-
if (status.state !== "connected" || !requestedProfile || status.accessProfileAccepted !== false) {
|
|
53045
|
-
return void 0;
|
|
53046
|
-
}
|
|
53047
|
-
return "The relay needs an update before this access profile can be shown to connected clients. Local profile enforcement remains active.";
|
|
53048
|
-
}
|
|
53049
|
-
function workspaceLabelNotice(status, requestedLabel) {
|
|
53050
|
-
if (status.state !== "connected" || !requestedLabel || status.workspaceLabelAccepted !== false) {
|
|
53051
|
-
return void 0;
|
|
53052
|
-
}
|
|
53053
|
-
return "The relay needs an update before workspace labels are available. This workspace is online without the requested label.";
|
|
53054
|
-
}
|
|
53055
|
-
var legacyRelayNotice = "The relay needs an update before this computer can expose several workspaces at once.";
|
|
53056
|
-
function combinedCompatibilityNotice(labelNotice, profileNotice, includeLegacyRelayNotice) {
|
|
53057
|
-
const messages = [
|
|
53058
|
-
labelNotice,
|
|
53059
|
-
profileNotice,
|
|
53060
|
-
includeLegacyRelayNotice ? legacyRelayNotice : void 0
|
|
53061
|
-
].filter((message) => Boolean(message));
|
|
53062
|
-
return messages.length > 0 ? messages.join(" ") : void 0;
|
|
53063
|
-
}
|
|
53064
52350
|
function statusMessage(status, connectedBefore) {
|
|
53065
52351
|
if (status.state === "connecting") return "Connecting to Glossa...";
|
|
53066
52352
|
if (status.state === "connected") {
|
|
@@ -53075,15 +52361,12 @@ function statusMessage(status, connectedBefore) {
|
|
|
53075
52361
|
async function connectRemoteWorker(endpoints, device, worker, options, signal, onConnected) {
|
|
53076
52362
|
let connectionState;
|
|
53077
52363
|
let connectedBefore = false;
|
|
53078
|
-
let labelNoticeShown = false;
|
|
53079
|
-
let profileNoticeShown = false;
|
|
53080
|
-
let legacyNoticeShown = false;
|
|
53081
52364
|
let connectHintTask;
|
|
53082
52365
|
const remoteWorker = new RemoteWorker({
|
|
53083
52366
|
origin: endpoints.workerOrigin,
|
|
53084
52367
|
deviceToken: device.token,
|
|
53085
52368
|
...options.workerVersion ? { workerVersion: options.workerVersion } : {},
|
|
53086
|
-
|
|
52369
|
+
accessProfile: options.accessProfile ?? DEFAULT_WORKER_ACCESS_PROFILE,
|
|
53087
52370
|
...options.workspaceLabel ? { workspaceLabel: options.workspaceLabel } : {},
|
|
53088
52371
|
worker: visibleWorker(worker, options),
|
|
53089
52372
|
signal,
|
|
@@ -53097,25 +52380,7 @@ async function connectRemoteWorker(endpoints, device, worker, options, signal, o
|
|
|
53097
52380
|
} else {
|
|
53098
52381
|
options.onEvent?.({ type: "status", status });
|
|
53099
52382
|
}
|
|
53100
|
-
|
|
53101
|
-
const profileNotice = profileNoticeShown ? void 0 : accessProfileNotice(status, options.accessProfile);
|
|
53102
|
-
const includeLegacyNotice = status.state === "connected" && status.legacyRelay && !legacyNoticeShown;
|
|
53103
|
-
const compatibilityNotice = combinedCompatibilityNotice(
|
|
53104
|
-
labelNotice,
|
|
53105
|
-
profileNotice,
|
|
53106
|
-
includeLegacyNotice
|
|
53107
|
-
);
|
|
53108
|
-
if (labelNotice) labelNoticeShown = true;
|
|
53109
|
-
if (profileNotice) profileNoticeShown = true;
|
|
53110
|
-
if (includeLegacyNotice) legacyNoticeShown = true;
|
|
53111
|
-
if (compatibilityNotice) {
|
|
53112
|
-
report(
|
|
53113
|
-
options,
|
|
53114
|
-
{ type: "notice", message: compatibilityNotice },
|
|
53115
|
-
compatibilityNotice
|
|
53116
|
-
);
|
|
53117
|
-
}
|
|
53118
|
-
if (status.state === "connected" && !status.reconnected && !compatibilityNotice && shouldShowConnectHint(endpoints.relayOrigin) && !connectHintTask) {
|
|
52383
|
+
if (status.state === "connected" && !status.reconnected && shouldShowConnectHint(endpoints.relayOrigin) && !connectHintTask) {
|
|
53119
52384
|
connectHintTask = announceConnectHint(
|
|
53120
52385
|
connectHintStore(),
|
|
53121
52386
|
(message) => {
|
|
@@ -53280,12 +52545,12 @@ function formatByteCount(value) {
|
|
|
53280
52545
|
}
|
|
53281
52546
|
return `${(kibibytes / 1024).toFixed(1)} MiB`;
|
|
53282
52547
|
}
|
|
53283
|
-
function workspacePath(
|
|
53284
|
-
return
|
|
52548
|
+
function workspacePath(path12) {
|
|
52549
|
+
return path12 || ".";
|
|
53285
52550
|
}
|
|
53286
|
-
function pathSummary(
|
|
52551
|
+
function pathSummary(path12, details = []) {
|
|
53287
52552
|
return {
|
|
53288
|
-
target: `path ${quoteActivityInput(
|
|
52553
|
+
target: `path ${quoteActivityInput(path12)}`,
|
|
53289
52554
|
details,
|
|
53290
52555
|
truncation: "middle"
|
|
53291
52556
|
};
|
|
@@ -53577,7 +52842,6 @@ function primaryFooterHints() {
|
|
|
53577
52842
|
{ key: "W", label: "Workspace" },
|
|
53578
52843
|
{ key: "D", label: "Devices" },
|
|
53579
52844
|
{ key: "?", label: "Help" },
|
|
53580
|
-
{ key: "L", label: "Account sign out", tone: COLORS.coral },
|
|
53581
52845
|
{ key: "Q", label: "Quit", tone: COLORS.coral }
|
|
53582
52846
|
];
|
|
53583
52847
|
}
|
|
@@ -53648,12 +52912,6 @@ function buildFooterRows(state, usable) {
|
|
|
53648
52912
|
function promptText(state) {
|
|
53649
52913
|
if (state.busy) return { message: "Working\u2026" };
|
|
53650
52914
|
if (!state.prompt) return void 0;
|
|
53651
|
-
if (state.prompt.type === "logout") {
|
|
53652
|
-
return {
|
|
53653
|
-
message: "Sign out the CLI account and disconnect? Computer pairing stays active.",
|
|
53654
|
-
choices: "Y confirm N cancel"
|
|
53655
|
-
};
|
|
53656
|
-
}
|
|
53657
52915
|
if (state.prompt.type === "access-confirm") {
|
|
53658
52916
|
const detail = state.prompt.accessProfile === "system" ? "Commands will inherit this OS account's permissions." : "This allows guarded file writes.";
|
|
53659
52917
|
return {
|
|
@@ -53940,7 +53198,7 @@ function DevicesView({ state, usable, bodyBudget, color }) {
|
|
|
53940
53198
|
const devices = state.status?.devices.slice(window2.start, window2.end) ?? [];
|
|
53941
53199
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { height: bodyBudget, flexDirection: "column", flexShrink: 0, overflow: "hidden", children: [
|
|
53942
53200
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Blank, {}),
|
|
53943
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { color, children: "
|
|
53201
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { color, children: "Paired" }),
|
|
53944
53202
|
state.statusLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
53945
53203
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Blank, {}),
|
|
53946
53204
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { color: color ? COLORS.muted : void 0, children: "Loading devices\u2026" })
|
|
@@ -53948,7 +53206,7 @@ function DevicesView({ state, usable, bodyBudget, color }) {
|
|
|
53948
53206
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Blank, {}),
|
|
53949
53207
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { color: color ? COLORS.muted : void 0, children: "Devices are not loaded." })
|
|
53950
53208
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
53951
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { color: color ? COLORS.ink : void 0, wrap: "truncate", children: state.
|
|
53209
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { color: color ? COLORS.ink : void 0, wrap: "truncate", children: state.deviceName ?? "This computer" }),
|
|
53952
53210
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { color: color ? COLORS.muted : void 0, wrap: "truncate", children: state.status.relay }),
|
|
53953
53211
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Blank, {}),
|
|
53954
53212
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { color, children: "Overview" }),
|
|
@@ -54000,7 +53258,6 @@ function HelpView({ bodyBudget, color }) {
|
|
|
54000
53258
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { color, tone: COLORS.coral, children: "Manage" }),
|
|
54001
53259
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(HelpRow, { keyLabel: "\u2190/\u2192", label: "Change workspace access", color, tone: COLORS.coral }),
|
|
54002
53260
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(HelpRow, { keyLabel: "Enter/R", label: "Revoke selected device", color, tone: COLORS.coral }),
|
|
54003
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(HelpRow, { keyLabel: "L", label: "Account sign out", color, tone: COLORS.coral }),
|
|
54004
53261
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Blank, {}),
|
|
54005
53262
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { color, children: "App" }),
|
|
54006
53263
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(HelpRow, { keyLabel: "Q", label: "Disconnect and quit", color, tone: COLORS.coral }),
|
|
@@ -54089,9 +53346,9 @@ function HudScreen({ state, columns, rows, color = true, now = Date.now() }) {
|
|
|
54089
53346
|
] });
|
|
54090
53347
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { width: Math.max(8, columns), height: Math.max(6, rows), flexDirection: "column", children: margin > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Box_default, { marginLeft: margin, children: inner }) : inner });
|
|
54091
53348
|
}
|
|
54092
|
-
async function loadDevices(
|
|
54093
|
-
const current =
|
|
54094
|
-
|
|
53349
|
+
async function loadDevices(store2, actions, signal) {
|
|
53350
|
+
const current = store2.getSnapshot();
|
|
53351
|
+
store2.update((state) => ({
|
|
54095
53352
|
...state,
|
|
54096
53353
|
view: "devices",
|
|
54097
53354
|
prompt: void 0,
|
|
@@ -54099,20 +53356,20 @@ async function loadDevices(store3, actions, signal) {
|
|
|
54099
53356
|
}));
|
|
54100
53357
|
if (current.statusLoading) return;
|
|
54101
53358
|
if (current.connection !== "connected" && current.connection !== "retrying") {
|
|
54102
|
-
|
|
53359
|
+
store2.update((state) => ({
|
|
54103
53360
|
...state,
|
|
54104
53361
|
notice: "Devices are available after Glossa connects."
|
|
54105
53362
|
}));
|
|
54106
53363
|
return;
|
|
54107
53364
|
}
|
|
54108
|
-
|
|
53365
|
+
store2.update((state) => ({
|
|
54109
53366
|
...state,
|
|
54110
53367
|
statusLoading: true
|
|
54111
53368
|
}));
|
|
54112
53369
|
try {
|
|
54113
53370
|
const status = await actions.loadStatus(signal);
|
|
54114
53371
|
if (signal.aborted) return;
|
|
54115
|
-
|
|
53372
|
+
store2.update((state) => ({
|
|
54116
53373
|
...state,
|
|
54117
53374
|
status,
|
|
54118
53375
|
deviceSelection: Math.min(
|
|
@@ -54123,24 +53380,24 @@ async function loadDevices(store3, actions, signal) {
|
|
|
54123
53380
|
}));
|
|
54124
53381
|
} catch (error46) {
|
|
54125
53382
|
if (signal.aborted) return;
|
|
54126
|
-
|
|
53383
|
+
store2.update((state) => ({
|
|
54127
53384
|
...state,
|
|
54128
53385
|
statusLoading: false,
|
|
54129
53386
|
notice: error46 instanceof Error ? error46.message : String(error46)
|
|
54130
53387
|
}));
|
|
54131
53388
|
}
|
|
54132
53389
|
}
|
|
54133
|
-
function beginAccessChange(
|
|
53390
|
+
function beginAccessChange(store2, actions, accessProfile) {
|
|
54134
53391
|
actions.changeAccessProfile(accessProfile);
|
|
54135
|
-
|
|
53392
|
+
store2.update((state) => ({
|
|
54136
53393
|
...state,
|
|
54137
53394
|
pendingAccessProfile: accessProfile,
|
|
54138
53395
|
prompt: void 0,
|
|
54139
53396
|
notice: void 0
|
|
54140
53397
|
}));
|
|
54141
53398
|
}
|
|
54142
|
-
function HudRuntime({ store:
|
|
54143
|
-
const state = (0, import_react34.useSyncExternalStore)(
|
|
53399
|
+
function HudRuntime({ store: store2, actions, signal, stop }) {
|
|
53400
|
+
const state = (0, import_react34.useSyncExternalStore)(store2.subscribe, store2.getSnapshot, store2.getSnapshot);
|
|
54144
53401
|
const { columns, rows } = use_window_size_default();
|
|
54145
53402
|
const [, setClock] = (0, import_react34.useState)(0);
|
|
54146
53403
|
(0, import_react34.useEffect)(() => {
|
|
@@ -54149,7 +53406,7 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54149
53406
|
return () => clearInterval(timer);
|
|
54150
53407
|
}, [state.view, state.activities.length]);
|
|
54151
53408
|
(0, import_react34.useEffect)(() => {
|
|
54152
|
-
|
|
53409
|
+
store2.update((current) => {
|
|
54153
53410
|
let next = current;
|
|
54154
53411
|
const metrics = screenMetrics(current, columns, rows);
|
|
54155
53412
|
if (current.view === "activity") {
|
|
@@ -54171,9 +53428,9 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54171
53428
|
}
|
|
54172
53429
|
return next;
|
|
54173
53430
|
});
|
|
54174
|
-
}, [columns, rows,
|
|
53431
|
+
}, [columns, rows, store2]);
|
|
54175
53432
|
use_input_default((input, key) => {
|
|
54176
|
-
const current =
|
|
53433
|
+
const current = store2.getSnapshot();
|
|
54177
53434
|
const value = input.toLowerCase();
|
|
54178
53435
|
if (key.ctrl && value === "c" || value === "q") {
|
|
54179
53436
|
stop();
|
|
@@ -54182,21 +53439,17 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54182
53439
|
if (current.busy) return;
|
|
54183
53440
|
if (current.prompt) {
|
|
54184
53441
|
if (key.escape || value === "n") {
|
|
54185
|
-
|
|
53442
|
+
store2.update((state2) => ({ ...state2, prompt: void 0, notice: void 0 }));
|
|
54186
53443
|
return;
|
|
54187
53444
|
}
|
|
54188
53445
|
if (value !== "y") return;
|
|
54189
|
-
if (current.prompt.type === "logout") {
|
|
54190
|
-
stop("logout");
|
|
54191
|
-
return;
|
|
54192
|
-
}
|
|
54193
53446
|
if (current.prompt.type === "access-confirm") {
|
|
54194
|
-
beginAccessChange(
|
|
53447
|
+
beginAccessChange(store2, actions, current.prompt.accessProfile);
|
|
54195
53448
|
return;
|
|
54196
53449
|
}
|
|
54197
53450
|
const device = current.status?.devices[current.prompt.deviceIndex];
|
|
54198
53451
|
if (!device) return;
|
|
54199
|
-
|
|
53452
|
+
store2.update((state2) => ({
|
|
54200
53453
|
...state2,
|
|
54201
53454
|
busy: true,
|
|
54202
53455
|
prompt: void 0,
|
|
@@ -54204,13 +53457,13 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54204
53457
|
}));
|
|
54205
53458
|
void actions.revokeDevice(device.id, signal).then(async () => {
|
|
54206
53459
|
if (signal.aborted) return;
|
|
54207
|
-
|
|
54208
|
-
await loadDevices(
|
|
53460
|
+
store2.update((state2) => ({ ...state2, busy: false }));
|
|
53461
|
+
await loadDevices(store2, actions, signal);
|
|
54209
53462
|
if (signal.aborted) return;
|
|
54210
|
-
|
|
53463
|
+
store2.update((state2) => ({ ...state2, notice: `Revoked ${device.name}.` }));
|
|
54211
53464
|
}).catch((error46) => {
|
|
54212
53465
|
if (signal.aborted) return;
|
|
54213
|
-
|
|
53466
|
+
store2.update((state2) => ({
|
|
54214
53467
|
...state2,
|
|
54215
53468
|
busy: false,
|
|
54216
53469
|
notice: error46 instanceof Error ? error46.message : String(error46)
|
|
@@ -54224,7 +53477,7 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54224
53477
|
const maxPage = activityMaxPage(current.activities.length, capacity);
|
|
54225
53478
|
const page = key.upArrow ? Math.min(maxPage, current.activityPage + 1) : Math.max(0, current.activityPage - 1);
|
|
54226
53479
|
if (page !== current.activityPage) {
|
|
54227
|
-
|
|
53480
|
+
store2.update((state2) => ({ ...state2, activityPage: page }));
|
|
54228
53481
|
}
|
|
54229
53482
|
return;
|
|
54230
53483
|
}
|
|
@@ -54236,7 +53489,7 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54236
53489
|
}
|
|
54237
53490
|
const selection = key.upArrow ? Math.max(0, current.deviceSelection - 1) : Math.min(total - 1, current.deviceSelection + 1);
|
|
54238
53491
|
if (selection !== current.deviceSelection) {
|
|
54239
|
-
|
|
53492
|
+
store2.update((state2) => ({ ...state2, deviceSelection: selection, notice: void 0 }));
|
|
54240
53493
|
}
|
|
54241
53494
|
return;
|
|
54242
53495
|
}
|
|
@@ -54245,9 +53498,9 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54245
53498
|
const accessProfile = adjacentAccessProfile(current.accessProfile, direction);
|
|
54246
53499
|
if (!accessProfile) return;
|
|
54247
53500
|
if (direction < 0) {
|
|
54248
|
-
beginAccessChange(
|
|
53501
|
+
beginAccessChange(store2, actions, accessProfile);
|
|
54249
53502
|
} else {
|
|
54250
|
-
|
|
53503
|
+
store2.update((state2) => ({
|
|
54251
53504
|
...state2,
|
|
54252
53505
|
prompt: { type: "access-confirm", accessProfile },
|
|
54253
53506
|
notice: void 0
|
|
@@ -54256,11 +53509,11 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54256
53509
|
return;
|
|
54257
53510
|
}
|
|
54258
53511
|
if (key.escape) {
|
|
54259
|
-
|
|
53512
|
+
store2.update((state2) => ({ ...state2, view: "workspace", notice: void 0 }));
|
|
54260
53513
|
return;
|
|
54261
53514
|
}
|
|
54262
53515
|
if (value === "a") {
|
|
54263
|
-
|
|
53516
|
+
store2.update((state2) => ({
|
|
54264
53517
|
...state2,
|
|
54265
53518
|
view: "activity",
|
|
54266
53519
|
activityPage: current.view === "activity" ? state2.activityPage : 0,
|
|
@@ -54269,7 +53522,7 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54269
53522
|
return;
|
|
54270
53523
|
}
|
|
54271
53524
|
if (value === "w") {
|
|
54272
|
-
|
|
53525
|
+
store2.update((state2) => ({
|
|
54273
53526
|
...state2,
|
|
54274
53527
|
view: "workspace",
|
|
54275
53528
|
prompt: void 0,
|
|
@@ -54279,27 +53532,27 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54279
53532
|
}
|
|
54280
53533
|
if (value === "d") {
|
|
54281
53534
|
if (current.status) {
|
|
54282
|
-
|
|
53535
|
+
store2.update((state2) => ({
|
|
54283
53536
|
...state2,
|
|
54284
53537
|
view: "devices",
|
|
54285
53538
|
prompt: void 0,
|
|
54286
53539
|
notice: void 0
|
|
54287
53540
|
}));
|
|
54288
53541
|
} else {
|
|
54289
|
-
void loadDevices(
|
|
53542
|
+
void loadDevices(store2, actions, signal);
|
|
54290
53543
|
}
|
|
54291
53544
|
return;
|
|
54292
53545
|
}
|
|
54293
53546
|
if ((key.return || input === "\r" || input === "\n" || value === "r") && current.view === "devices") {
|
|
54294
53547
|
const count = current.status?.devices.length ?? 0;
|
|
54295
53548
|
if (count === 0) {
|
|
54296
|
-
|
|
53549
|
+
store2.update((state2) => ({ ...state2, notice: "There are no devices to revoke." }));
|
|
54297
53550
|
return;
|
|
54298
53551
|
}
|
|
54299
53552
|
const metrics = screenMetrics(current, columns, rows);
|
|
54300
53553
|
const capacity = statusDeviceCapacity(current, metrics.bodyBudget, metrics.usable);
|
|
54301
53554
|
const selection = Math.min(current.deviceSelection, count - 1);
|
|
54302
|
-
|
|
53555
|
+
store2.update((state2) => capacity === 0 ? { ...state2, notice: "Increase the terminal height to choose a device." } : {
|
|
54303
53556
|
...state2,
|
|
54304
53557
|
deviceSelection: selection,
|
|
54305
53558
|
prompt: { type: "revoke-confirm", deviceIndex: selection },
|
|
@@ -54307,16 +53560,8 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54307
53560
|
});
|
|
54308
53561
|
return;
|
|
54309
53562
|
}
|
|
54310
|
-
if (value === "l") {
|
|
54311
|
-
store3.update((state2) => ({
|
|
54312
|
-
...state2,
|
|
54313
|
-
prompt: { type: "logout" },
|
|
54314
|
-
notice: void 0
|
|
54315
|
-
}));
|
|
54316
|
-
return;
|
|
54317
|
-
}
|
|
54318
53563
|
if (input === "?") {
|
|
54319
|
-
|
|
53564
|
+
store2.update((state2) => ({
|
|
54320
53565
|
...state2,
|
|
54321
53566
|
view: "help",
|
|
54322
53567
|
notice: void 0
|
|
@@ -54339,11 +53584,9 @@ async function runSessionHud(actions, input = process.stdin, output = process.st
|
|
|
54339
53584
|
throw new Error("Glossa requires an interactive terminal.");
|
|
54340
53585
|
}
|
|
54341
53586
|
const controller = new AbortController();
|
|
54342
|
-
const
|
|
54343
|
-
let exitAction = "quit";
|
|
53587
|
+
const store2 = new HudStore(actions.workspace);
|
|
54344
53588
|
let instance;
|
|
54345
|
-
const stop = (
|
|
54346
|
-
exitAction = action;
|
|
53589
|
+
const stop = () => {
|
|
54347
53590
|
controller.abort();
|
|
54348
53591
|
instance?.unmount();
|
|
54349
53592
|
};
|
|
@@ -54355,7 +53598,7 @@ async function runSessionHud(actions, input = process.stdin, output = process.st
|
|
|
54355
53598
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
54356
53599
|
HudRuntime,
|
|
54357
53600
|
{
|
|
54358
|
-
store:
|
|
53601
|
+
store: store2,
|
|
54359
53602
|
actions,
|
|
54360
53603
|
signal: controller.signal,
|
|
54361
53604
|
stop
|
|
@@ -54372,13 +53615,13 @@ async function runSessionHud(actions, input = process.stdin, output = process.st
|
|
|
54372
53615
|
maxFps: 30
|
|
54373
53616
|
}
|
|
54374
53617
|
);
|
|
54375
|
-
const session = actions.run(controller.signal, (event) =>
|
|
53618
|
+
const session = actions.run(controller.signal, (event) => store2.event(event)).then(() => {
|
|
54376
53619
|
if (!controller.signal.aborted) {
|
|
54377
|
-
|
|
53620
|
+
store2.update((state) => ({ ...state, connection: "disconnected" }));
|
|
54378
53621
|
}
|
|
54379
53622
|
}).catch((error46) => {
|
|
54380
53623
|
if (!controller.signal.aborted) {
|
|
54381
|
-
|
|
53624
|
+
store2.update((state) => ({
|
|
54382
53625
|
...state,
|
|
54383
53626
|
connection: "error",
|
|
54384
53627
|
message: error46 instanceof Error ? error46.message : String(error46)
|
|
@@ -54391,7 +53634,6 @@ async function runSessionHud(actions, input = process.stdin, output = process.st
|
|
|
54391
53634
|
try {
|
|
54392
53635
|
await instance.waitUntilExit();
|
|
54393
53636
|
await session;
|
|
54394
|
-
return exitAction;
|
|
54395
53637
|
} finally {
|
|
54396
53638
|
controller.abort();
|
|
54397
53639
|
process.removeListener("SIGINT", stopFromSignal);
|
|
@@ -54401,10 +53643,10 @@ async function runSessionHud(actions, input = process.stdin, output = process.st
|
|
|
54401
53643
|
|
|
54402
53644
|
// src/update-service.ts
|
|
54403
53645
|
var import_semver = __toESM(require_semver2(), 1);
|
|
54404
|
-
import { spawn as
|
|
53646
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
54405
53647
|
import { createHash as createHash2 } from "node:crypto";
|
|
54406
53648
|
import { chmod as chmod3, readdir as readdir2, rename as rename2, rm as rm5, writeFile as writeFile6 } from "node:fs/promises";
|
|
54407
|
-
import
|
|
53649
|
+
import path8 from "node:path";
|
|
54408
53650
|
var PACKAGE_NAME = "@ariobarin/glossa";
|
|
54409
53651
|
var DEFAULT_REGISTRY_URL = "https://registry.npmjs.org/@ariobarin%2Fglossa";
|
|
54410
53652
|
var DEFAULT_RELEASE_BASE_URL = "https://github.com/ariobarin/glossa/releases/download";
|
|
@@ -54481,8 +53723,8 @@ async function cleanupUpdateBackups(distribution, options = {}) {
|
|
|
54481
53723
|
const platform2 = options.platform ?? process.platform;
|
|
54482
53724
|
if (distribution !== "standalone" || platform2 !== "win32") return;
|
|
54483
53725
|
const executable = options.executablePath ?? process.execPath;
|
|
54484
|
-
const directory =
|
|
54485
|
-
const prefix = `${
|
|
53726
|
+
const directory = path8.dirname(executable);
|
|
53727
|
+
const prefix = `${path8.basename(executable)}.old-`;
|
|
54486
53728
|
let entries;
|
|
54487
53729
|
try {
|
|
54488
53730
|
entries = await readdir2(directory, { withFileTypes: true });
|
|
@@ -54495,7 +53737,7 @@ async function cleanupUpdateBackups(distribution, options = {}) {
|
|
|
54495
53737
|
const ownerPid = backupOwnerPid(entry.name, prefix);
|
|
54496
53738
|
return ownerPid !== null && !processIsAlive(ownerPid);
|
|
54497
53739
|
}).map(async (entry) => {
|
|
54498
|
-
await rm5(
|
|
53740
|
+
await rm5(path8.join(directory, entry.name), { force: true }).catch(() => void 0);
|
|
54499
53741
|
})
|
|
54500
53742
|
);
|
|
54501
53743
|
}
|
|
@@ -54536,7 +53778,7 @@ function npmInstallInvocation(version2, platform2 = process.platform, commandShe
|
|
|
54536
53778
|
async function runNpmInstall(version2) {
|
|
54537
53779
|
const { command, args } = npmInstallInvocation(version2);
|
|
54538
53780
|
await new Promise((resolve, reject) => {
|
|
54539
|
-
const child =
|
|
53781
|
+
const child = spawn2(command, args, { stdio: "inherit" });
|
|
54540
53782
|
child.once("error", reject);
|
|
54541
53783
|
child.once("close", (status) => {
|
|
54542
53784
|
if (status === 0) resolve();
|
|
@@ -54571,9 +53813,9 @@ async function installStandaloneUpdate(info, options) {
|
|
|
54571
53813
|
if (actual !== expected) {
|
|
54572
53814
|
throw new Error("Glossa refused to update because the SHA-256 checksum did not match.");
|
|
54573
53815
|
}
|
|
54574
|
-
const download =
|
|
54575
|
-
|
|
54576
|
-
`${
|
|
53816
|
+
const download = path8.join(
|
|
53817
|
+
path8.dirname(destination),
|
|
53818
|
+
`${path8.basename(destination)}.download-${process.pid}`
|
|
54577
53819
|
);
|
|
54578
53820
|
try {
|
|
54579
53821
|
await writeFile6(download, binary, { mode: 493 });
|
|
@@ -54600,13 +53842,13 @@ async function installUpdate(info, distribution, options = {}) {
|
|
|
54600
53842
|
|
|
54601
53843
|
// src/update-state.ts
|
|
54602
53844
|
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
|
|
53845
|
+
import path9 from "node:path";
|
|
54604
53846
|
var UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
54605
53847
|
function defaultUpdateChannel(version2) {
|
|
54606
53848
|
return version2.includes("-") ? "beta" : "stable";
|
|
54607
53849
|
}
|
|
54608
53850
|
function updateStateFile() {
|
|
54609
|
-
return
|
|
53851
|
+
return path9.join(configDirectory(), "updates.json");
|
|
54610
53852
|
}
|
|
54611
53853
|
function isPolicy(value) {
|
|
54612
53854
|
return value === "notify" || value === "auto" || value === "off";
|
|
@@ -54625,12 +53867,10 @@ async function loadUpdateState(currentVersion, file2 = updateStateFile()) {
|
|
|
54625
53867
|
try {
|
|
54626
53868
|
const parsed = JSON.parse(await readFile5(file2, "utf8"));
|
|
54627
53869
|
const lastCheckedAt = optionalString(parsed.lastCheckedAt);
|
|
54628
|
-
const latestVersion = optionalString(parsed.latestVersion);
|
|
54629
53870
|
return {
|
|
54630
53871
|
policy: isPolicy(parsed.policy) ? parsed.policy : defaults.policy,
|
|
54631
53872
|
channel: isChannel(parsed.channel) ? parsed.channel : defaults.channel,
|
|
54632
|
-
...lastCheckedAt ? { lastCheckedAt } : {}
|
|
54633
|
-
...latestVersion ? { latestVersion } : {}
|
|
53873
|
+
...lastCheckedAt ? { lastCheckedAt } : {}
|
|
54634
53874
|
};
|
|
54635
53875
|
} catch (error46) {
|
|
54636
53876
|
if (error46.code === "ENOENT" || error46 instanceof SyntaxError) {
|
|
@@ -54640,7 +53880,7 @@ async function loadUpdateState(currentVersion, file2 = updateStateFile()) {
|
|
|
54640
53880
|
}
|
|
54641
53881
|
}
|
|
54642
53882
|
async function saveUpdateState(state, file2 = updateStateFile()) {
|
|
54643
|
-
const directory =
|
|
53883
|
+
const directory = path9.dirname(file2);
|
|
54644
53884
|
const temporary = `${file2}.${process.pid}.tmp`;
|
|
54645
53885
|
await mkdir6(directory, { recursive: true, mode: 448 });
|
|
54646
53886
|
try {
|
|
@@ -54664,11 +53904,10 @@ async function configureUpdates(currentVersion, changes, file2 = updateStateFile
|
|
|
54664
53904
|
await saveUpdateState(state, file2);
|
|
54665
53905
|
return state;
|
|
54666
53906
|
}
|
|
54667
|
-
async function recordUpdateCheck(currentVersion,
|
|
53907
|
+
async function recordUpdateCheck(currentVersion, checkedAt = /* @__PURE__ */ new Date(), file2 = updateStateFile()) {
|
|
54668
53908
|
const state = {
|
|
54669
53909
|
...await loadUpdateState(currentVersion, file2),
|
|
54670
|
-
lastCheckedAt: checkedAt.toISOString()
|
|
54671
|
-
latestVersion
|
|
53910
|
+
lastCheckedAt: checkedAt.toISOString()
|
|
54672
53911
|
};
|
|
54673
53912
|
await saveUpdateState(state, file2);
|
|
54674
53913
|
return state;
|
|
@@ -54695,41 +53934,18 @@ async function unpairComputer(dependencies = {}) {
|
|
|
54695
53934
|
log("Unpaired this computer from Glossa.");
|
|
54696
53935
|
}
|
|
54697
53936
|
|
|
54698
|
-
// src/usage-store.ts
|
|
54699
|
-
import { appendFile, chmod as chmod5, mkdir as mkdir7, readFile as readFile6 } from "node:fs/promises";
|
|
54700
|
-
import path11 from "node:path";
|
|
54701
|
-
function usageFile() {
|
|
54702
|
-
return path11.join(configDirectory(), "usage.jsonl");
|
|
54703
|
-
}
|
|
54704
|
-
async function recordUsageEvent(event, file2 = usageFile()) {
|
|
54705
|
-
const directory = path11.dirname(file2);
|
|
54706
|
-
await mkdir7(directory, { recursive: true, mode: 448 });
|
|
54707
|
-
await appendFile(file2, `${JSON.stringify(event)}
|
|
54708
|
-
`, {
|
|
54709
|
-
encoding: "utf8",
|
|
54710
|
-
mode: 384
|
|
54711
|
-
});
|
|
54712
|
-
if (process.platform !== "win32") await chmod5(file2, 384);
|
|
54713
|
-
}
|
|
54714
|
-
async function recordSessionUsage(at = /* @__PURE__ */ new Date(), file2 = usageFile()) {
|
|
54715
|
-
await recordUsageEvent({ v: 1, at: at.toISOString(), kind: "session" }, file2);
|
|
54716
|
-
}
|
|
54717
|
-
async function recordToolUsage(tool, ok, at = /* @__PURE__ */ new Date(), file2 = usageFile()) {
|
|
54718
|
-
await recordUsageEvent({ v: 1, at: at.toISOString(), kind: "tool", tool, ok }, file2);
|
|
54719
|
-
}
|
|
54720
|
-
|
|
54721
53937
|
// src/worker/root-selection.ts
|
|
54722
53938
|
import { realpath as realpath2 } from "node:fs/promises";
|
|
54723
53939
|
import os6 from "node:os";
|
|
54724
|
-
import
|
|
53940
|
+
import path10 from "node:path";
|
|
54725
53941
|
function containsPath(root, candidate) {
|
|
54726
|
-
const relative =
|
|
54727
|
-
return relative !== "" && relative !== ".." && !relative.startsWith(`..${
|
|
53942
|
+
const relative = path10.relative(root, candidate);
|
|
53943
|
+
return relative !== "" && relative !== ".." && !relative.startsWith(`..${path10.sep}`) && !path10.isAbsolute(relative);
|
|
54728
53944
|
}
|
|
54729
53945
|
async function rejectImplicitHomeAncestor(root) {
|
|
54730
53946
|
const homes = await Promise.all(
|
|
54731
53947
|
[os6.homedir(), accountHomeDirectory()].map(
|
|
54732
|
-
async (home) => await realpath2(home).catch(() =>
|
|
53948
|
+
async (home) => await realpath2(home).catch(() => path10.resolve(home))
|
|
54733
53949
|
)
|
|
54734
53950
|
);
|
|
54735
53951
|
if (homes.some((home) => containsPath(root, home))) {
|
|
@@ -54747,10 +53963,10 @@ async function selectExposureRoot(explicitPath, cwd2 = process.cwd()) {
|
|
|
54747
53963
|
|
|
54748
53964
|
// src/worker/workspace-lease.ts
|
|
54749
53965
|
import { createHash as createHash3 } from "node:crypto";
|
|
54750
|
-
import { lstat as lstat3, mkdir as
|
|
53966
|
+
import { lstat as lstat3, mkdir as mkdir7, rm as rm7, stat as stat4 } from "node:fs/promises";
|
|
54751
53967
|
import net from "node:net";
|
|
54752
|
-
import
|
|
54753
|
-
import { setTimeout as
|
|
53968
|
+
import path11 from "node:path";
|
|
53969
|
+
import { setTimeout as delay4 } from "node:timers/promises";
|
|
54754
53970
|
var GUARD_STALE_MS = 5e3;
|
|
54755
53971
|
var GUARD_RETRY_MS = 10;
|
|
54756
53972
|
var GUARD_TIMEOUT_MS = 1e4;
|
|
@@ -54773,12 +53989,12 @@ function workspaceIdentity(root) {
|
|
|
54773
53989
|
}
|
|
54774
53990
|
function defaultLeaseDirectory() {
|
|
54775
53991
|
if (typeof process.getuid === "function") {
|
|
54776
|
-
return
|
|
53992
|
+
return path11.join("/tmp", `glossa-workspaces-${process.getuid()}`);
|
|
54777
53993
|
}
|
|
54778
|
-
return
|
|
53994
|
+
return path11.join(accountHomeDirectory(), ".glossa-workspace-leases");
|
|
54779
53995
|
}
|
|
54780
53996
|
async function ensureLeaseDirectory(directory) {
|
|
54781
|
-
await
|
|
53997
|
+
await mkdir7(directory, { recursive: true, mode: 448 });
|
|
54782
53998
|
const directoryStat = await lstat3(directory);
|
|
54783
53999
|
if (directoryStat.isSymbolicLink() || !directoryStat.isDirectory()) {
|
|
54784
54000
|
throw new Error("The Glossa workspace lease location is not a private directory.");
|
|
@@ -54795,14 +54011,14 @@ function endpointFor(identity, directory) {
|
|
|
54795
54011
|
if (process.platform === "win32") {
|
|
54796
54012
|
return `\\\\.\\pipe\\glossa-workspace-${identity}`;
|
|
54797
54013
|
}
|
|
54798
|
-
return
|
|
54014
|
+
return path11.join(directory, `${identity}.sock`);
|
|
54799
54015
|
}
|
|
54800
54016
|
async function acquireGuard2(directory, identity) {
|
|
54801
|
-
const guard =
|
|
54017
|
+
const guard = path11.join(directory, `${identity}.guard`);
|
|
54802
54018
|
const deadline = Date.now() + GUARD_TIMEOUT_MS;
|
|
54803
54019
|
while (true) {
|
|
54804
54020
|
try {
|
|
54805
|
-
await
|
|
54021
|
+
await mkdir7(guard, { mode: 448 });
|
|
54806
54022
|
return async () => await rm7(guard, { recursive: true, force: true });
|
|
54807
54023
|
} catch (error46) {
|
|
54808
54024
|
if (error46.code !== "EEXIST") throw error46;
|
|
@@ -54822,14 +54038,14 @@ async function acquireGuard2(directory, identity) {
|
|
|
54822
54038
|
"Timed out while checking whether this workspace is already active."
|
|
54823
54039
|
);
|
|
54824
54040
|
}
|
|
54825
|
-
await
|
|
54041
|
+
await delay4(GUARD_RETRY_MS);
|
|
54826
54042
|
}
|
|
54827
54043
|
}
|
|
54828
54044
|
}
|
|
54829
54045
|
function createLeaseServer() {
|
|
54830
54046
|
return net.createServer((socket) => socket.destroy());
|
|
54831
54047
|
}
|
|
54832
|
-
async function listen(server,
|
|
54048
|
+
async function listen(server, endpoint) {
|
|
54833
54049
|
await new Promise((resolve, reject) => {
|
|
54834
54050
|
const onError = (error46) => {
|
|
54835
54051
|
server.removeListener("listening", onListening);
|
|
@@ -54841,12 +54057,12 @@ async function listen(server, endpoint3) {
|
|
|
54841
54057
|
};
|
|
54842
54058
|
server.once("error", onError);
|
|
54843
54059
|
server.once("listening", onListening);
|
|
54844
|
-
server.listen(
|
|
54060
|
+
server.listen(endpoint);
|
|
54845
54061
|
});
|
|
54846
54062
|
}
|
|
54847
|
-
async function endpointIsActive(
|
|
54063
|
+
async function endpointIsActive(endpoint) {
|
|
54848
54064
|
return await new Promise((resolve) => {
|
|
54849
|
-
const socket = net.createConnection(
|
|
54065
|
+
const socket = net.createConnection(endpoint);
|
|
54850
54066
|
let settled = false;
|
|
54851
54067
|
const finish = (active) => {
|
|
54852
54068
|
if (settled) return;
|
|
@@ -54869,14 +54085,14 @@ async function acquireWorkspaceLease(root, options = {}) {
|
|
|
54869
54085
|
const userDirectory = options.directory ?? defaultLeaseDirectory();
|
|
54870
54086
|
await ensureLeaseDirectory(userDirectory);
|
|
54871
54087
|
const identity = workspaceIdentity(root);
|
|
54872
|
-
const
|
|
54088
|
+
const endpoint = endpointFor(identity, userDirectory);
|
|
54873
54089
|
const releaseGuard = await acquireGuard2(userDirectory, identity);
|
|
54874
54090
|
let server;
|
|
54875
54091
|
try {
|
|
54876
54092
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
54877
54093
|
server = createLeaseServer();
|
|
54878
54094
|
try {
|
|
54879
|
-
await listen(server,
|
|
54095
|
+
await listen(server, endpoint);
|
|
54880
54096
|
server.unref();
|
|
54881
54097
|
let released = false;
|
|
54882
54098
|
return {
|
|
@@ -54888,7 +54104,7 @@ async function acquireWorkspaceLease(root, options = {}) {
|
|
|
54888
54104
|
try {
|
|
54889
54105
|
await closeServer(server);
|
|
54890
54106
|
if (process.platform !== "win32") {
|
|
54891
|
-
await rm7(
|
|
54107
|
+
await rm7(endpoint, { force: true });
|
|
54892
54108
|
}
|
|
54893
54109
|
} finally {
|
|
54894
54110
|
await releaseGuard2();
|
|
@@ -54897,11 +54113,11 @@ async function acquireWorkspaceLease(root, options = {}) {
|
|
|
54897
54113
|
};
|
|
54898
54114
|
} catch (error46) {
|
|
54899
54115
|
await closeServer(server);
|
|
54900
|
-
if (await endpointIsActive(
|
|
54116
|
+
if (await endpointIsActive(endpoint)) {
|
|
54901
54117
|
throw new WorkspaceAlreadyActiveError();
|
|
54902
54118
|
}
|
|
54903
54119
|
if (error46.code !== "EADDRINUSE") throw error46;
|
|
54904
|
-
if (process.platform !== "win32") await rm7(
|
|
54120
|
+
if (process.platform !== "win32") await rm7(endpoint, { force: true });
|
|
54905
54121
|
}
|
|
54906
54122
|
}
|
|
54907
54123
|
throw new Error("Could not reserve this workspace for the current Glossa process.");
|
|
@@ -54911,15 +54127,12 @@ async function acquireWorkspaceLease(root, options = {}) {
|
|
|
54911
54127
|
}
|
|
54912
54128
|
|
|
54913
54129
|
// src/main.ts
|
|
54914
|
-
var VERSION = "0.2.
|
|
54130
|
+
var VERSION = "0.2.2";
|
|
54915
54131
|
var DISTRIBUTION = "npm";
|
|
54916
54132
|
var HELP = `Glossa ${VERSION}
|
|
54917
54133
|
|
|
54918
54134
|
Usage:
|
|
54919
54135
|
glossa [--access <read-only|workspace|system>] [--label <name>] [directory]
|
|
54920
|
-
glossa status
|
|
54921
|
-
glossa devices revoke <id>
|
|
54922
|
-
glossa logout
|
|
54923
54136
|
glossa unpair
|
|
54924
54137
|
glossa update [--check]
|
|
54925
54138
|
glossa update --policy <notify|auto|off>
|
|
@@ -54941,68 +54154,18 @@ Keys:
|
|
|
54941
54154
|
\u2190\u2192 change workspace access
|
|
54942
54155
|
Enter/r revoke selected device
|
|
54943
54156
|
Esc workspace
|
|
54944
|
-
l account sign out
|
|
54945
54157
|
? help
|
|
54946
54158
|
q disconnect and quit`;
|
|
54947
|
-
async function
|
|
54948
|
-
const
|
|
54949
|
-
const cancel = () => controller.abort();
|
|
54950
|
-
process.once("SIGINT", cancel);
|
|
54951
|
-
try {
|
|
54952
|
-
return await action(controller.signal);
|
|
54953
|
-
} finally {
|
|
54954
|
-
process.removeListener("SIGINT", cancel);
|
|
54955
|
-
}
|
|
54956
|
-
}
|
|
54957
|
-
async function authenticatedSession(signal) {
|
|
54958
|
-
if (signal) return await signedInSession({ ...loadAuthConfig(), signal });
|
|
54959
|
-
return await withLoginSignal(
|
|
54960
|
-
async (loginSignal) => await signedInSession({ ...loadAuthConfig(), signal: loginSignal })
|
|
54961
|
-
);
|
|
54962
|
-
}
|
|
54963
|
-
async function authenticatedCredentials(signal) {
|
|
54964
|
-
return (await authenticatedSession(signal)).credentials;
|
|
54965
|
-
}
|
|
54966
|
-
async function showStatus() {
|
|
54967
|
-
const credentials = await authenticatedCredentials();
|
|
54968
|
-
const endpoints = loadRelayEndpoints();
|
|
54969
|
-
const status = await new WorkspaceStatusService(
|
|
54970
|
-
credentials,
|
|
54971
|
-
endpoints
|
|
54972
|
-
).refresh();
|
|
54973
|
-
for (const line of formatStatus(status)) console.log(line);
|
|
54974
|
-
}
|
|
54975
|
-
function hudStatus(status) {
|
|
54976
|
-
return {
|
|
54977
|
-
...status,
|
|
54978
|
-
devices: status.devices.map((device) => ({
|
|
54979
|
-
id: device.id,
|
|
54980
|
-
name: device.name,
|
|
54981
|
-
platform: device.platform ?? "Unknown platform",
|
|
54982
|
-
lastSeen: formatRelativeTime(device.lastSeenAt),
|
|
54983
|
-
status: deviceStatus(device)
|
|
54984
|
-
}))
|
|
54985
|
-
};
|
|
54986
|
-
}
|
|
54987
|
-
async function revokeKnownDevice(deviceId) {
|
|
54988
|
-
const credentials = await authenticatedCredentials();
|
|
54989
|
-
await revokeDevice(loadRelayEndpoints(), credentials, deviceId);
|
|
54990
|
-
}
|
|
54991
|
-
async function runWorkspaceSession(path14, label, accessProfile) {
|
|
54992
|
-
const root = await selectExposureRoot(path14);
|
|
54159
|
+
async function runWorkspaceSession(path12, label, accessProfile) {
|
|
54160
|
+
const root = await selectExposureRoot(path12);
|
|
54993
54161
|
const lease = await acquireWorkspaceLease(root);
|
|
54994
|
-
let usageWrites = Promise.resolve();
|
|
54995
|
-
const queueUsage = (write) => {
|
|
54996
|
-
usageWrites = usageWrites.then(write).catch(() => void 0);
|
|
54997
|
-
};
|
|
54998
54162
|
try {
|
|
54999
54163
|
const endpoints = loadRelayEndpoints();
|
|
55000
54164
|
const device = await deviceForSession(endpoints);
|
|
55001
54165
|
let postExitNotice;
|
|
55002
54166
|
let requestedAccessProfile = accessProfile;
|
|
55003
54167
|
let activeSessionController;
|
|
55004
|
-
|
|
55005
|
-
const exitAction = await runSessionHud({
|
|
54168
|
+
await runSessionHud({
|
|
55006
54169
|
workspace: root,
|
|
55007
54170
|
...label ? { workspaceLabel: label } : {},
|
|
55008
54171
|
run: async (signal, onEvent) => {
|
|
@@ -55022,9 +54185,6 @@ async function runWorkspaceSession(path14, label, accessProfile) {
|
|
|
55022
54185
|
signal: sessionController.signal,
|
|
55023
54186
|
onEvent: (event) => {
|
|
55024
54187
|
postExitNotice = retainPostExitNotice(postExitNotice, event);
|
|
55025
|
-
if (event.type === "activity" && event.phase === "returned") {
|
|
55026
|
-
queueUsage(async () => await recordToolUsage(event.job.type, event.ok));
|
|
55027
|
-
}
|
|
55028
54188
|
onEvent(event);
|
|
55029
54189
|
},
|
|
55030
54190
|
quiet: true,
|
|
@@ -55046,16 +54206,24 @@ async function runWorkspaceSession(path14, label, accessProfile) {
|
|
|
55046
54206
|
}
|
|
55047
54207
|
},
|
|
55048
54208
|
loadStatus: async (signal) => {
|
|
55049
|
-
const
|
|
55050
|
-
|
|
55051
|
-
|
|
55052
|
-
|
|
54209
|
+
const withSignal = async (input, init) => await fetch(input, { ...init, signal });
|
|
54210
|
+
const devices = (await listDevices(endpoints, `Device ${device.token}`, withSignal)).filter((entry) => entry.revokedAt === null);
|
|
54211
|
+
return {
|
|
54212
|
+
relay: endpoints.relayOrigin,
|
|
54213
|
+
activeWorkers: devices.some((entry) => entry.activeWorkers === null) ? null : devices.reduce((total, entry) => total + entry.activeWorkers, 0),
|
|
54214
|
+
devices: devices.map((entry) => ({
|
|
54215
|
+
id: entry.id,
|
|
54216
|
+
name: entry.name,
|
|
54217
|
+
platform: entry.platform ?? "Unknown platform",
|
|
54218
|
+
lastSeen: formatRelativeTime(entry.lastSeenAt),
|
|
54219
|
+
status: deviceStatus(entry)
|
|
54220
|
+
}))
|
|
54221
|
+
};
|
|
55053
54222
|
},
|
|
55054
54223
|
revokeDevice: async (deviceId, signal) => {
|
|
55055
|
-
const credentials = await authenticatedCredentials(signal);
|
|
55056
54224
|
await revokeDevice(
|
|
55057
54225
|
endpoints,
|
|
55058
|
-
|
|
54226
|
+
`Device ${device.token}`,
|
|
55059
54227
|
deviceId,
|
|
55060
54228
|
async (input, init) => await fetch(input, { ...init, signal })
|
|
55061
54229
|
);
|
|
@@ -55067,20 +54235,18 @@ async function runWorkspaceSession(path14, label, accessProfile) {
|
|
|
55067
54235
|
}
|
|
55068
54236
|
});
|
|
55069
54237
|
if (postExitNotice) console.error(postExitNotice);
|
|
55070
|
-
if (exitAction === "logout") await logoutFromGlossa();
|
|
55071
54238
|
} finally {
|
|
55072
|
-
await usageWrites;
|
|
55073
54239
|
await lease.release();
|
|
55074
54240
|
}
|
|
55075
54241
|
}
|
|
55076
|
-
async function runWorkspace(
|
|
54242
|
+
async function runWorkspace(path12, label, accessProfile) {
|
|
55077
54243
|
await withWorkspaceLease(
|
|
55078
|
-
async () => await runWorkspaceSession(
|
|
54244
|
+
async () => await runWorkspaceSession(path12, label, accessProfile)
|
|
55079
54245
|
);
|
|
55080
54246
|
}
|
|
55081
54247
|
async function refreshUpdateInfo(timeoutMs) {
|
|
55082
54248
|
const info = await loadUpdateInfo(timeoutMs);
|
|
55083
|
-
await recordUpdateCheck(VERSION
|
|
54249
|
+
await recordUpdateCheck(VERSION);
|
|
55084
54250
|
return info;
|
|
55085
54251
|
}
|
|
55086
54252
|
async function loadUpdateInfo(timeoutMs) {
|
|
@@ -55135,11 +54301,11 @@ async function updateBeforeWorkspace() {
|
|
|
55135
54301
|
return false;
|
|
55136
54302
|
}
|
|
55137
54303
|
if (!info.updateAvailable) {
|
|
55138
|
-
await recordUpdateCheck(VERSION
|
|
54304
|
+
await recordUpdateCheck(VERSION);
|
|
55139
54305
|
return false;
|
|
55140
54306
|
}
|
|
55141
54307
|
if (state.policy === "notify") {
|
|
55142
|
-
await recordUpdateCheck(VERSION
|
|
54308
|
+
await recordUpdateCheck(VERSION);
|
|
55143
54309
|
console.error(
|
|
55144
54310
|
`Glossa ${info.availableVersion} is available. Run glossa update after disconnecting.`
|
|
55145
54311
|
);
|
|
@@ -55174,17 +54340,10 @@ async function main() {
|
|
|
55174
54340
|
invocation.label,
|
|
55175
54341
|
invocation.accessProfile
|
|
55176
54342
|
);
|
|
55177
|
-
} else if (invocation.command === "status") {
|
|
55178
|
-
await showStatus();
|
|
55179
|
-
} else if (invocation.command === "logout") {
|
|
55180
|
-
await logoutFromGlossa();
|
|
55181
54343
|
} else if (invocation.command === "unpair") {
|
|
55182
54344
|
await unpairComputer();
|
|
55183
54345
|
} else if (invocation.command === "update") {
|
|
55184
54346
|
await runUpdateCommand(invocation);
|
|
55185
|
-
} else {
|
|
55186
|
-
await revokeKnownDevice(invocation.deviceId);
|
|
55187
|
-
console.log(`Revoked device ${invocation.deviceId}. Running workspaces on it are disconnected.`);
|
|
55188
54347
|
}
|
|
55189
54348
|
}
|
|
55190
54349
|
main().catch((error46) => {
|