@ariobarin/glossa 0.2.1 → 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 +503 -1322
- 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,536 +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 authorizeWithDeviceFlow(options, dependencies = {}) {
|
|
27151
|
-
const fetchRequest = dependencies.fetch ?? fetch;
|
|
27152
|
-
const wait = dependencies.delay ?? defaultDelay;
|
|
27153
|
-
const browse = dependencies.openBrowser ?? openBrowser;
|
|
27154
|
-
const now = dependencies.now ?? Date.now;
|
|
27155
|
-
const log = dependencies.log ?? console.log;
|
|
27156
|
-
try {
|
|
27157
|
-
assertActive(options.signal);
|
|
27158
|
-
const code = await postForm(
|
|
27159
|
-
fetchRequest,
|
|
27160
|
-
endpoint2(options.issuer, "oauth/device/code"),
|
|
27161
|
-
{
|
|
27162
|
-
client_id: options.clientId,
|
|
27163
|
-
audience: options.audience,
|
|
27164
|
-
scope: options.scope
|
|
27165
|
-
},
|
|
27166
|
-
options.signal
|
|
27167
|
-
);
|
|
27168
|
-
const verificationUrl = code.verification_uri_complete ?? code.verification_uri;
|
|
27169
|
-
const opened = await browse(verificationUrl);
|
|
27170
|
-
log(opened ? "Opened your browser for Glossa login." : "Open this URL to sign in:");
|
|
27171
|
-
log(verificationUrl);
|
|
27172
|
-
if (!code.verification_uri_complete) log(`Code: ${code.user_code}`);
|
|
27173
|
-
const startedAt = now();
|
|
27174
|
-
let intervalSeconds = Math.max(code.interval ?? 5, 1);
|
|
27175
|
-
while (now() - startedAt < code.expires_in * 1e3) {
|
|
27176
|
-
assertActive(options.signal);
|
|
27177
|
-
await wait(intervalSeconds * 1e3, options.signal);
|
|
27178
|
-
assertActive(options.signal);
|
|
27179
|
-
const response = await fetchRequest(endpoint2(options.issuer, "oauth/token"), {
|
|
27180
|
-
method: "POST",
|
|
27181
|
-
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
27182
|
-
body: new URLSearchParams({
|
|
27183
|
-
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
27184
|
-
device_code: code.device_code,
|
|
27185
|
-
client_id: options.clientId
|
|
27186
|
-
}),
|
|
27187
|
-
...options.signal ? { signal: options.signal } : {}
|
|
27188
|
-
});
|
|
27189
|
-
const data = await response.json();
|
|
27190
|
-
if (response.ok && "access_token" in data) {
|
|
27191
|
-
const grantedScope = data.scope ?? options.scope;
|
|
27192
|
-
if (!grantedScopesSatisfyRequest(
|
|
27193
|
-
grantedScope,
|
|
27194
|
-
options.scope,
|
|
27195
|
-
Boolean(data.refresh_token)
|
|
27196
|
-
)) {
|
|
27197
|
-
throw new Error("Auth0 did not grant the permissions Glossa requires.");
|
|
27198
|
-
}
|
|
27199
|
-
return {
|
|
27200
|
-
issuer: options.issuer,
|
|
27201
|
-
clientId: options.clientId,
|
|
27202
|
-
audience: options.audience,
|
|
27203
|
-
accessToken: data.access_token,
|
|
27204
|
-
...data.refresh_token ? { 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
|
-
}
|
|
27211
|
-
const error46 = data;
|
|
27212
|
-
if (error46.error === "authorization_pending") continue;
|
|
27213
|
-
if (error46.error === "slow_down") {
|
|
27214
|
-
intervalSeconds += 5;
|
|
27215
|
-
continue;
|
|
27216
|
-
}
|
|
27217
|
-
if (error46.error === "access_denied") throw new Error("Login was denied.");
|
|
27218
|
-
if (error46.error === "expired_token") throw new Error("The login code expired.");
|
|
27219
|
-
throw new Error(error46.error_description ?? error46.error);
|
|
27220
|
-
}
|
|
27221
|
-
throw new Error("The login code expired.");
|
|
27222
|
-
} catch (error46) {
|
|
27223
|
-
if (options.signal?.aborted || error46 instanceof Error && error46.name === "AbortError") {
|
|
27224
|
-
throw canceledError();
|
|
27225
|
-
}
|
|
27226
|
-
throw error46;
|
|
27227
|
-
}
|
|
27228
|
-
}
|
|
27229
|
-
async function loginWithDeviceFlow(options, dependencies = {}) {
|
|
27230
|
-
const credentials = await authorizeWithDeviceFlow(options, dependencies);
|
|
27231
|
-
if (!credentials.refreshToken) {
|
|
27232
|
-
throw new Error("Auth0 did not issue a refresh token.");
|
|
27233
|
-
}
|
|
27234
|
-
const save = dependencies.saveCredentials ?? saveCredentials;
|
|
27235
|
-
await save(credentials);
|
|
27236
|
-
(dependencies.log ?? console.log)("Signed in to Glossa.");
|
|
27237
|
-
}
|
|
27238
|
-
|
|
27239
|
-
// src/auth-login.ts
|
|
27240
|
-
function normalizedIssuer(value) {
|
|
27241
|
-
return value.replace(/\/+$/, "");
|
|
27242
|
-
}
|
|
27243
|
-
function credentialsMatchLoginOptions(credentials, options) {
|
|
27244
|
-
return normalizedIssuer(credentials.issuer) === normalizedIssuer(options.issuer) && credentials.clientId === options.clientId && credentials.audience === options.audience && scopesMatch(credentials.requestedScope, options.scope) && grantedScopesSatisfyRequest(
|
|
27245
|
-
credentials.scope,
|
|
27246
|
-
options.scope,
|
|
27247
|
-
Boolean(credentials.refreshToken)
|
|
27248
|
-
);
|
|
27249
|
-
}
|
|
27250
|
-
async function signedInSession(options, dependencies = {}) {
|
|
27251
|
-
const load = dependencies.loadCredentials ?? loadCredentials;
|
|
27252
|
-
const validate2 = dependencies.validCredentials ?? validCredentials;
|
|
27253
|
-
const login = dependencies.loginWithDeviceFlow ?? loginWithDeviceFlow;
|
|
27254
|
-
const loaded = await load();
|
|
27255
|
-
if (loaded && credentialsMatchLoginOptions(loaded.credentials, options)) {
|
|
27256
|
-
try {
|
|
27257
|
-
const credentials = await validate2(
|
|
27258
|
-
loaded.credentials,
|
|
27259
|
-
options.signal ? { signal: options.signal } : {}
|
|
27260
|
-
);
|
|
27261
|
-
return { credentials, loginPerformed: false };
|
|
27262
|
-
} catch (error46) {
|
|
27263
|
-
if (!(error46 instanceof SessionExpiredError)) throw error46;
|
|
27264
|
-
}
|
|
27265
|
-
}
|
|
27266
|
-
await login(options);
|
|
27267
|
-
const completed = await load();
|
|
27268
|
-
if (!completed) throw new Error("Glossa could not load the completed login.");
|
|
27269
|
-
return {
|
|
27270
|
-
credentials: await validate2(
|
|
27271
|
-
completed.credentials,
|
|
27272
|
-
options.signal ? { signal: options.signal } : {}
|
|
27273
|
-
),
|
|
27274
|
-
loginPerformed: true
|
|
27275
|
-
};
|
|
27276
|
-
}
|
|
27277
|
-
|
|
27278
26748
|
// ../../node_modules/zod/v4/classic/external.js
|
|
27279
26749
|
var external_exports = {};
|
|
27280
26750
|
__export(external_exports, {
|
|
@@ -28006,10 +27476,10 @@ function mergeDefs(...defs) {
|
|
|
28006
27476
|
function cloneDef(schema) {
|
|
28007
27477
|
return mergeDefs(schema._zod.def);
|
|
28008
27478
|
}
|
|
28009
|
-
function getElementAtPath(obj,
|
|
28010
|
-
if (!
|
|
27479
|
+
function getElementAtPath(obj, path12) {
|
|
27480
|
+
if (!path12)
|
|
28011
27481
|
return obj;
|
|
28012
|
-
return
|
|
27482
|
+
return path12.reduce((acc, key) => acc?.[key], obj);
|
|
28013
27483
|
}
|
|
28014
27484
|
function promiseAllObject(promisesObj) {
|
|
28015
27485
|
const keys = Object.keys(promisesObj);
|
|
@@ -28370,11 +27840,11 @@ function aborted(x, startIndex = 0) {
|
|
|
28370
27840
|
}
|
|
28371
27841
|
return false;
|
|
28372
27842
|
}
|
|
28373
|
-
function prefixIssues(
|
|
27843
|
+
function prefixIssues(path12, issues) {
|
|
28374
27844
|
return issues.map((iss) => {
|
|
28375
27845
|
var _a;
|
|
28376
27846
|
(_a = iss).path ?? (_a.path = []);
|
|
28377
|
-
iss.path.unshift(
|
|
27847
|
+
iss.path.unshift(path12);
|
|
28378
27848
|
return iss;
|
|
28379
27849
|
});
|
|
28380
27850
|
}
|
|
@@ -28536,7 +28006,7 @@ function formatError(error46, mapper = (issue2) => issue2.message) {
|
|
|
28536
28006
|
}
|
|
28537
28007
|
function treeifyError(error46, mapper = (issue2) => issue2.message) {
|
|
28538
28008
|
const result = { errors: [] };
|
|
28539
|
-
const processError = (error47,
|
|
28009
|
+
const processError = (error47, path12 = []) => {
|
|
28540
28010
|
var _a, _b;
|
|
28541
28011
|
for (const issue2 of error47.issues) {
|
|
28542
28012
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
@@ -28546,7 +28016,7 @@ function treeifyError(error46, mapper = (issue2) => issue2.message) {
|
|
|
28546
28016
|
} else if (issue2.code === "invalid_element") {
|
|
28547
28017
|
processError({ issues: issue2.issues }, issue2.path);
|
|
28548
28018
|
} else {
|
|
28549
|
-
const fullpath = [...
|
|
28019
|
+
const fullpath = [...path12, ...issue2.path];
|
|
28550
28020
|
if (fullpath.length === 0) {
|
|
28551
28021
|
result.errors.push(mapper(issue2));
|
|
28552
28022
|
continue;
|
|
@@ -28578,8 +28048,8 @@ function treeifyError(error46, mapper = (issue2) => issue2.message) {
|
|
|
28578
28048
|
}
|
|
28579
28049
|
function toDotPath(_path) {
|
|
28580
28050
|
const segs = [];
|
|
28581
|
-
const
|
|
28582
|
-
for (const seg of
|
|
28051
|
+
const path12 = _path.map((seg) => typeof seg === "object" ? seg.key : seg);
|
|
28052
|
+
for (const seg of path12) {
|
|
28583
28053
|
if (typeof seg === "number")
|
|
28584
28054
|
segs.push(`[${seg}]`);
|
|
28585
28055
|
else if (typeof seg === "symbol")
|
|
@@ -40129,9 +39599,12 @@ var UsageError = class extends Error {
|
|
|
40129
39599
|
};
|
|
40130
39600
|
var retiredCommands = /* @__PURE__ */ new Set([
|
|
40131
39601
|
"completions",
|
|
39602
|
+
"devices",
|
|
40132
39603
|
"doctor",
|
|
40133
39604
|
"login",
|
|
40134
|
-
"
|
|
39605
|
+
"logout",
|
|
39606
|
+
"start",
|
|
39607
|
+
"status"
|
|
40135
39608
|
]);
|
|
40136
39609
|
function parseWorkspace(args) {
|
|
40137
39610
|
let selectedPath;
|
|
@@ -40189,12 +39662,6 @@ function parseWorkspace(args) {
|
|
|
40189
39662
|
accessProfile
|
|
40190
39663
|
};
|
|
40191
39664
|
}
|
|
40192
|
-
function parseDevices(args) {
|
|
40193
|
-
if (args[0] === "revoke" && args.length === 2) {
|
|
40194
|
-
return { command: "devices", action: "revoke", deviceId: args[1] };
|
|
40195
|
-
}
|
|
40196
|
-
throw new UsageError("Use: glossa devices revoke <id>.");
|
|
40197
|
-
}
|
|
40198
39665
|
function parseUpdate(args) {
|
|
40199
39666
|
if (args.length === 0) return { command: "update", action: "install" };
|
|
40200
39667
|
let check3 = false;
|
|
@@ -40254,15 +39721,6 @@ function parseInvocation(args) {
|
|
|
40254
39721
|
return { command: "version" };
|
|
40255
39722
|
}
|
|
40256
39723
|
if (command === "--") return parseWorkspace(args);
|
|
40257
|
-
if (command === "status") {
|
|
40258
|
-
noOptions("Status", options);
|
|
40259
|
-
return { command };
|
|
40260
|
-
}
|
|
40261
|
-
if (command === "devices") return parseDevices(options);
|
|
40262
|
-
if (command === "logout") {
|
|
40263
|
-
noOptions("Logout", options);
|
|
40264
|
-
return { command };
|
|
40265
|
-
}
|
|
40266
39724
|
if (command === "unpair") {
|
|
40267
39725
|
noOptions("Unpair", options);
|
|
40268
39726
|
return { command };
|
|
@@ -40291,47 +39749,9 @@ function formatRelativeTime(iso, now = Date.now()) {
|
|
|
40291
39749
|
const days = Math.round(hours / 24);
|
|
40292
39750
|
return `${days}d ago`;
|
|
40293
39751
|
}
|
|
40294
|
-
function formatDeviceRow(device, now = Date.now()) {
|
|
40295
|
-
const platform2 = device.platform ?? "unknown platform";
|
|
40296
|
-
return `${device.id} ${device.name} ${platform2} last seen ${formatRelativeTime(device.lastSeenAt, now)} ${deviceStatus(device)}`;
|
|
40297
|
-
}
|
|
40298
|
-
|
|
40299
|
-
// src/logout.ts
|
|
40300
|
-
function browserLogoutUrl(issuer) {
|
|
40301
|
-
return new URL(
|
|
40302
|
-
"v2/logout",
|
|
40303
|
-
issuer.endsWith("/") ? issuer : `${issuer}/`
|
|
40304
|
-
).toString();
|
|
40305
|
-
}
|
|
40306
|
-
async function logoutFromGlossa(dependencies = {}) {
|
|
40307
|
-
const remove = dependencies.deleteCredentials ?? deleteCredentials;
|
|
40308
|
-
const peek = dependencies.peekCredentials ?? peekCredentials;
|
|
40309
|
-
const browse = dependencies.openBrowser ?? openBrowser;
|
|
40310
|
-
const log = dependencies.log ?? console.log;
|
|
40311
|
-
let stored = null;
|
|
40312
|
-
let present = true;
|
|
40313
|
-
try {
|
|
40314
|
-
stored = await peek();
|
|
40315
|
-
present = stored !== null;
|
|
40316
|
-
} catch {
|
|
40317
|
-
}
|
|
40318
|
-
const issuer = dependencies.issuer ?? stored?.credentials.issuer;
|
|
40319
|
-
await remove();
|
|
40320
|
-
log(
|
|
40321
|
-
present ? "Signed out of the Glossa account. Computer pairing stays active." : "Already signed out of the Glossa account. Computer pairing stays active."
|
|
40322
|
-
);
|
|
40323
|
-
const url2 = browserLogoutUrl(issuer ?? loadAuthConfig().issuer);
|
|
40324
|
-
const opened = await browse(url2);
|
|
40325
|
-
if (opened) {
|
|
40326
|
-
log("Opened Glossa browser sign-out.");
|
|
40327
|
-
} else {
|
|
40328
|
-
log("Open this URL to finish signing out in your browser:");
|
|
40329
|
-
log(url2);
|
|
40330
|
-
}
|
|
40331
|
-
}
|
|
40332
39752
|
|
|
40333
39753
|
// src/relay-client.ts
|
|
40334
|
-
import
|
|
39754
|
+
import os from "node:os";
|
|
40335
39755
|
var DEFAULT_RELAY_ORIGIN = "https://mcp.glossa.sh";
|
|
40336
39756
|
function isLoopback(hostname3) {
|
|
40337
39757
|
return hostname3 === "localhost" || hostname3 === "127.0.0.1" || hostname3 === "[::1]";
|
|
@@ -40375,10 +39795,61 @@ function loadRelayEndpoints(environment = process.env) {
|
|
|
40375
39795
|
return { relayOrigin, workerOrigin };
|
|
40376
39796
|
}
|
|
40377
39797
|
function defaultDeviceName() {
|
|
40378
|
-
return deviceNameSchema.parse(
|
|
39798
|
+
return deviceNameSchema.parse(os.hostname());
|
|
39799
|
+
}
|
|
39800
|
+
var PairingCodeExpiredError = class extends Error {
|
|
39801
|
+
};
|
|
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}.`);
|
|
40379
39807
|
}
|
|
40380
|
-
function
|
|
40381
|
-
|
|
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);
|
|
40382
39853
|
if (status === 403 && data.error === "account_disabled") {
|
|
40383
39854
|
return new Error("This Glossa account is disabled.");
|
|
40384
39855
|
}
|
|
@@ -40401,7 +39872,7 @@ function relayError(status, data) {
|
|
|
40401
39872
|
function validNullableString(value) {
|
|
40402
39873
|
return value === null || typeof value === "string";
|
|
40403
39874
|
}
|
|
40404
|
-
function
|
|
39875
|
+
function parseDevices(value) {
|
|
40405
39876
|
if (!Array.isArray(value)) {
|
|
40406
39877
|
throw new Error("The Glossa relay returned an invalid device list response.");
|
|
40407
39878
|
}
|
|
@@ -40416,63 +39887,18 @@ function parseDevices2(value) {
|
|
|
40416
39887
|
activeWorkers: device.activeWorkers ?? null
|
|
40417
39888
|
}));
|
|
40418
39889
|
}
|
|
40419
|
-
|
|
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) {
|
|
40420
39892
|
const response = await fetchRequest(`${endpoints.relayOrigin}/v1/devices`, {
|
|
40421
|
-
headers: {
|
|
40422
|
-
authorization: `${credentials.tokenType} ${credentials.accessToken}`
|
|
40423
|
-
}
|
|
39893
|
+
headers: { authorization }
|
|
40424
39894
|
});
|
|
40425
39895
|
let data = {};
|
|
40426
39896
|
try {
|
|
40427
39897
|
data = await response.json();
|
|
40428
39898
|
} catch {
|
|
40429
39899
|
}
|
|
40430
|
-
if (!response.ok) throw relayError(response.status, data);
|
|
40431
|
-
return
|
|
40432
|
-
}
|
|
40433
|
-
async function enrollDevice(endpoints, credentials, deviceName, fetchRequest = fetch) {
|
|
40434
|
-
const baseName = deviceNameSchema.parse(deviceName);
|
|
40435
|
-
let name = baseName;
|
|
40436
|
-
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
40437
|
-
const response = await fetchRequest(`${endpoints.relayOrigin}/v1/devices/enroll`, {
|
|
40438
|
-
method: "POST",
|
|
40439
|
-
headers: {
|
|
40440
|
-
authorization: `${credentials.tokenType} ${credentials.accessToken}`,
|
|
40441
|
-
"content-type": "application/json"
|
|
40442
|
-
},
|
|
40443
|
-
body: JSON.stringify({ name, platform: `${process.platform}-${process.arch}` })
|
|
40444
|
-
});
|
|
40445
|
-
let data = {};
|
|
40446
|
-
try {
|
|
40447
|
-
data = await response.json();
|
|
40448
|
-
} catch {
|
|
40449
|
-
}
|
|
40450
|
-
if (response.status === 409 && data.error === "device_name_conflict" && attempt < 2) {
|
|
40451
|
-
const activeNames = new Set(
|
|
40452
|
-
(await listDevices(endpoints, credentials, fetchRequest)).filter((device) => device.revokedAt === null).map((device) => device.name)
|
|
40453
|
-
);
|
|
40454
|
-
for (let suffix = 2; ; suffix += 1) {
|
|
40455
|
-
const ending = `-${suffix}`;
|
|
40456
|
-
const candidate = `${baseName.slice(0, 80 - ending.length)}${ending}`;
|
|
40457
|
-
if (!activeNames.has(candidate)) {
|
|
40458
|
-
name = deviceNameSchema.parse(candidate);
|
|
40459
|
-
break;
|
|
40460
|
-
}
|
|
40461
|
-
}
|
|
40462
|
-
continue;
|
|
40463
|
-
}
|
|
40464
|
-
if (!response.ok) throw relayError(response.status, data);
|
|
40465
|
-
if (typeof data.device?.id !== "string" || typeof data.device.name !== "string" || typeof data.device_token !== "string") {
|
|
40466
|
-
throw new Error("The Glossa relay returned an invalid device enrollment response.");
|
|
40467
|
-
}
|
|
40468
|
-
return {
|
|
40469
|
-
relayOrigin: endpoints.relayOrigin,
|
|
40470
|
-
deviceId: data.device.id,
|
|
40471
|
-
deviceName: data.device.name,
|
|
40472
|
-
token: data.device_token
|
|
40473
|
-
};
|
|
40474
|
-
}
|
|
40475
|
-
throw new Error("Glossa could not enroll this computer.");
|
|
39900
|
+
if (!response.ok) throw relayError(response.status, data, PAIRING_REJECTED);
|
|
39901
|
+
return parseDevices(data.devices);
|
|
40476
39902
|
}
|
|
40477
39903
|
async function revokePairedDevice(endpoints, device, fetchRequest = fetch) {
|
|
40478
39904
|
const response = await fetchRequest(`${endpoints.relayOrigin}/device`, {
|
|
@@ -40485,103 +39911,17 @@ async function revokePairedDevice(endpoints, device, fetchRequest = fetch) {
|
|
|
40485
39911
|
}
|
|
40486
39912
|
throw new Error(`The Glossa relay returned HTTP ${response.status}.`);
|
|
40487
39913
|
}
|
|
40488
|
-
async function revokeDevice(endpoints,
|
|
39914
|
+
async function revokeDevice(endpoints, authorization, deviceId, fetchRequest = fetch) {
|
|
40489
39915
|
const response = await fetchRequest(`${endpoints.relayOrigin}/v1/devices/${encodeURIComponent(deviceId)}`, {
|
|
40490
39916
|
method: "DELETE",
|
|
40491
|
-
headers: {
|
|
40492
|
-
authorization: `${credentials.tokenType} ${credentials.accessToken}`
|
|
40493
|
-
}
|
|
39917
|
+
headers: { authorization }
|
|
40494
39918
|
});
|
|
40495
39919
|
if (!response.ok) {
|
|
40496
39920
|
const data = await response.json().catch(() => ({}));
|
|
40497
|
-
throw relayError(response.status, data);
|
|
39921
|
+
throw relayError(response.status, data, PAIRING_REJECTED);
|
|
40498
39922
|
}
|
|
40499
39923
|
}
|
|
40500
39924
|
|
|
40501
|
-
// src/status-display.ts
|
|
40502
|
-
function formatStatus(status) {
|
|
40503
|
-
const lines = [
|
|
40504
|
-
`Signed in as ${status.account}.`,
|
|
40505
|
-
`Relay connected: ${status.relay}`
|
|
40506
|
-
];
|
|
40507
|
-
if (status.activeWorkers === null) {
|
|
40508
|
-
lines.push("Active workspaces: unavailable");
|
|
40509
|
-
} else if (status.activeWorkers === 0) {
|
|
40510
|
-
lines.push("No active workspaces. Run glossa from the project folder you want to expose.");
|
|
40511
|
-
} else {
|
|
40512
|
-
lines.push(`Active workspaces: ${status.activeWorkers}`);
|
|
40513
|
-
}
|
|
40514
|
-
if (status.devices.length === 0) lines.push("No active devices.");
|
|
40515
|
-
else lines.push(...status.devices.map((device) => formatDeviceRow(device)));
|
|
40516
|
-
return lines;
|
|
40517
|
-
}
|
|
40518
|
-
|
|
40519
|
-
// src/status-service.ts
|
|
40520
|
-
function accountLabel(profile) {
|
|
40521
|
-
return profile.email ?? profile.name ?? profile.sub;
|
|
40522
|
-
}
|
|
40523
|
-
function activeWorkerCount(devices) {
|
|
40524
|
-
if (devices.some((device) => device.activeWorkers === null)) return null;
|
|
40525
|
-
return devices.reduce((sum, device) => sum + device.activeWorkers, 0);
|
|
40526
|
-
}
|
|
40527
|
-
var WorkspaceStatusService = class {
|
|
40528
|
-
constructor(credentials, endpoints, dependencies = {}) {
|
|
40529
|
-
this.endpoints = endpoints;
|
|
40530
|
-
this.dependencies = dependencies;
|
|
40531
|
-
this.#credentials = credentials;
|
|
40532
|
-
}
|
|
40533
|
-
endpoints;
|
|
40534
|
-
dependencies;
|
|
40535
|
-
#credentials;
|
|
40536
|
-
#inFlight;
|
|
40537
|
-
async refresh(signal) {
|
|
40538
|
-
if (this.#inFlight) return await this.#inFlight;
|
|
40539
|
-
const pending = this.#load(signal);
|
|
40540
|
-
this.#inFlight = pending;
|
|
40541
|
-
try {
|
|
40542
|
-
return await pending;
|
|
40543
|
-
} finally {
|
|
40544
|
-
if (this.#inFlight === pending) this.#inFlight = void 0;
|
|
40545
|
-
}
|
|
40546
|
-
}
|
|
40547
|
-
async #load(signal) {
|
|
40548
|
-
const validate2 = this.dependencies.validCredentials ?? validCredentials;
|
|
40549
|
-
const devicesForAccount = this.dependencies.listDevices ?? listDevices;
|
|
40550
|
-
const profileForAccount = this.dependencies.loadUserProfile ?? loadUserProfile;
|
|
40551
|
-
const baseFetch = this.dependencies.fetch ?? fetch;
|
|
40552
|
-
const fetchRequest = signal ? async (input, init) => await baseFetch(input, { ...init, signal }) : baseFetch;
|
|
40553
|
-
this.#credentials = await validate2(
|
|
40554
|
-
this.#credentials,
|
|
40555
|
-
signal ? { signal } : {}
|
|
40556
|
-
);
|
|
40557
|
-
const requestCredentials = this.#credentials;
|
|
40558
|
-
const profileRequest = profileForAccount(
|
|
40559
|
-
requestCredentials,
|
|
40560
|
-
signal ? { signal, fetch: fetchRequest } : { fetch: fetchRequest }
|
|
40561
|
-
).catch((error46) => {
|
|
40562
|
-
if (signal?.aborted) throw error46;
|
|
40563
|
-
return null;
|
|
40564
|
-
});
|
|
40565
|
-
const devicesRequest = devicesForAccount(
|
|
40566
|
-
this.endpoints,
|
|
40567
|
-
requestCredentials,
|
|
40568
|
-
fetchRequest
|
|
40569
|
-
);
|
|
40570
|
-
const [profile, devices] = await Promise.all([
|
|
40571
|
-
profileRequest,
|
|
40572
|
-
devicesRequest
|
|
40573
|
-
]);
|
|
40574
|
-
if (profile) this.#credentials = profile.credentials;
|
|
40575
|
-
const activeDevices = devices.filter((device) => device.revokedAt === null);
|
|
40576
|
-
return {
|
|
40577
|
-
account: profile ? accountLabel(profile.profile) : "Account unavailable",
|
|
40578
|
-
relay: this.endpoints.relayOrigin,
|
|
40579
|
-
activeWorkers: activeWorkerCount(activeDevices),
|
|
40580
|
-
devices: activeDevices
|
|
40581
|
-
};
|
|
40582
|
-
}
|
|
40583
|
-
};
|
|
40584
|
-
|
|
40585
39925
|
// src/ui-hud.tsx
|
|
40586
39926
|
var import_react34 = __toESM(require_react(), 1);
|
|
40587
39927
|
|
|
@@ -40735,7 +40075,7 @@ __export(base_exports, {
|
|
|
40735
40075
|
synchronizedOutput: () => synchronizedOutput
|
|
40736
40076
|
});
|
|
40737
40077
|
import process2 from "node:process";
|
|
40738
|
-
import
|
|
40078
|
+
import os2 from "node:os";
|
|
40739
40079
|
|
|
40740
40080
|
// ../../node_modules/environment/index.js
|
|
40741
40081
|
var isBrowser = globalThis.window?.document !== void 0;
|
|
@@ -40834,7 +40174,7 @@ var isOldWindows = () => {
|
|
|
40834
40174
|
if (isBrowser || !isWindows2) {
|
|
40835
40175
|
return false;
|
|
40836
40176
|
}
|
|
40837
|
-
const parts =
|
|
40177
|
+
const parts = os2.release().split(".");
|
|
40838
40178
|
const major = Number(parts[0]);
|
|
40839
40179
|
const build = Number(parts[2] ?? 0);
|
|
40840
40180
|
if (major < 10) {
|
|
@@ -45830,7 +45170,7 @@ var ansi_styles_default2 = ansiStyles2;
|
|
|
45830
45170
|
|
|
45831
45171
|
// ../../node_modules/chalk/source/vendor/supports-color/index.js
|
|
45832
45172
|
import process5 from "node:process";
|
|
45833
|
-
import
|
|
45173
|
+
import os3 from "node:os";
|
|
45834
45174
|
import tty2 from "node:tty";
|
|
45835
45175
|
function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process5.argv) {
|
|
45836
45176
|
const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--";
|
|
@@ -45895,7 +45235,7 @@ function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) {
|
|
|
45895
45235
|
return min;
|
|
45896
45236
|
}
|
|
45897
45237
|
if (process5.platform === "win32") {
|
|
45898
|
-
const osRelease =
|
|
45238
|
+
const osRelease = os3.release().split(".");
|
|
45899
45239
|
if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) {
|
|
45900
45240
|
return Number(osRelease[2]) >= 14931 ? 3 : 2;
|
|
45901
45241
|
}
|
|
@@ -47671,8 +47011,8 @@ function Text({ color, backgroundColor, dimColor = false, bold = false, italic =
|
|
|
47671
47011
|
}
|
|
47672
47012
|
|
|
47673
47013
|
// ../../node_modules/ink/build/components/ErrorOverview.js
|
|
47674
|
-
var cleanupPath = (
|
|
47675
|
-
return
|
|
47014
|
+
var cleanupPath = (path12) => {
|
|
47015
|
+
return path12?.replace(`file://${cwd()}/`, "");
|
|
47676
47016
|
};
|
|
47677
47017
|
var stackUtils = new import_stack_utils.default({
|
|
47678
47018
|
cwd: cwd(),
|
|
@@ -49774,14 +49114,136 @@ var import_react33 = __toESM(require_react(), 1);
|
|
|
49774
49114
|
|
|
49775
49115
|
// src/first-run.ts
|
|
49776
49116
|
import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
|
|
49777
|
-
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
|
|
49778
49240
|
var CONNECT_HINT_FILE = "connect-hint-shown";
|
|
49779
49241
|
var CONNECT_HINT_URL = "https://glossa.sh/docs/quickstart";
|
|
49780
49242
|
function shouldShowConnectHint(relayOrigin) {
|
|
49781
49243
|
return relayOrigin === DEFAULT_RELAY_ORIGIN;
|
|
49782
49244
|
}
|
|
49783
49245
|
function connectHintStore(directory = configDirectory()) {
|
|
49784
|
-
const file2 =
|
|
49246
|
+
const file2 = path2.join(directory, CONNECT_HINT_FILE);
|
|
49785
49247
|
return {
|
|
49786
49248
|
async exists() {
|
|
49787
49249
|
try {
|
|
@@ -49798,15 +49260,15 @@ function connectHintStore(directory = configDirectory()) {
|
|
|
49798
49260
|
}
|
|
49799
49261
|
};
|
|
49800
49262
|
}
|
|
49801
|
-
async function announceConnectHint(
|
|
49802
|
-
if (await
|
|
49263
|
+
async function announceConnectHint(store2, log) {
|
|
49264
|
+
if (await store2.exists()) return false;
|
|
49803
49265
|
log(`Next: return to ChatGPT and use Glossa. If it is not connected yet, follow ${CONNECT_HINT_URL}.`);
|
|
49804
|
-
await
|
|
49266
|
+
await store2.mark();
|
|
49805
49267
|
return true;
|
|
49806
49268
|
}
|
|
49807
49269
|
|
|
49808
49270
|
// src/device-store.ts
|
|
49809
|
-
import
|
|
49271
|
+
import path3 from "node:path";
|
|
49810
49272
|
var FILE_DEVICE_WARNING = "Warning: the operating-system credential store is unavailable. Glossa is using a mode-0600 device credential file.";
|
|
49811
49273
|
function parseDeviceCredential(value) {
|
|
49812
49274
|
let parsed;
|
|
@@ -49830,36 +49292,36 @@ function parseDeviceCredential(value) {
|
|
|
49830
49292
|
}
|
|
49831
49293
|
return parsed;
|
|
49832
49294
|
}
|
|
49833
|
-
var
|
|
49295
|
+
var store = new SecureStore({
|
|
49834
49296
|
account: "device",
|
|
49835
|
-
file:
|
|
49297
|
+
file: path3.join(configDirectory(), "device.json"),
|
|
49836
49298
|
warning: FILE_DEVICE_WARNING,
|
|
49837
49299
|
parse: parseDeviceCredential
|
|
49838
49300
|
});
|
|
49839
49301
|
async function loadDeviceCredential() {
|
|
49840
|
-
return (await
|
|
49302
|
+
return (await store.load())?.value ?? null;
|
|
49841
49303
|
}
|
|
49842
49304
|
async function saveDeviceCredential(credential) {
|
|
49843
|
-
await
|
|
49305
|
+
await store.save(credential);
|
|
49844
49306
|
}
|
|
49845
49307
|
async function deleteDeviceCredential() {
|
|
49846
|
-
await
|
|
49308
|
+
await store.delete();
|
|
49847
49309
|
}
|
|
49848
49310
|
|
|
49849
49311
|
// src/device-pairing-lock.ts
|
|
49850
49312
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
49851
49313
|
import { mkdir as mkdir4, readFile as readFile3, rm as rm3, stat, writeFile as writeFile4 } from "node:fs/promises";
|
|
49852
|
-
import
|
|
49853
|
-
import { setTimeout as
|
|
49314
|
+
import path5 from "node:path";
|
|
49315
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
49854
49316
|
|
|
49855
49317
|
// src/update-lock.ts
|
|
49856
49318
|
import { randomUUID } from "node:crypto";
|
|
49857
49319
|
import { mkdir as mkdir3, readdir, rm as rm2, writeFile as writeFile3 } from "node:fs/promises";
|
|
49858
|
-
import
|
|
49320
|
+
import path4 from "node:path";
|
|
49859
49321
|
var UPDATE_SUFFIX = ".update";
|
|
49860
49322
|
var SESSION_SUFFIX = ".session";
|
|
49861
49323
|
function updateRuntimeDirectory() {
|
|
49862
|
-
return
|
|
49324
|
+
return path4.join(configDirectory(), "runtime");
|
|
49863
49325
|
}
|
|
49864
49326
|
function processIsAlive(pid) {
|
|
49865
49327
|
if (!Number.isSafeInteger(pid) || pid <= 0) return false;
|
|
@@ -49889,7 +49351,7 @@ async function activeLeaseFiles(directory, suffix) {
|
|
|
49889
49351
|
for (const entry of entries) {
|
|
49890
49352
|
if (!entry.isFile() || !entry.name.endsWith(suffix)) continue;
|
|
49891
49353
|
const pid = leasePid(entry.name, suffix);
|
|
49892
|
-
const file2 =
|
|
49354
|
+
const file2 = path4.join(directory, entry.name);
|
|
49893
49355
|
if (pid !== null && processIsAlive(pid)) active.push(file2);
|
|
49894
49356
|
else await rm2(file2, { force: true });
|
|
49895
49357
|
}
|
|
@@ -49903,7 +49365,7 @@ async function activeSessionFiles(directory) {
|
|
|
49903
49365
|
}
|
|
49904
49366
|
async function createUpdateLock(directory) {
|
|
49905
49367
|
await mkdir3(directory, { recursive: true, mode: 448 });
|
|
49906
|
-
const lockFile =
|
|
49368
|
+
const lockFile = path4.join(
|
|
49907
49369
|
directory,
|
|
49908
49370
|
`${process.pid}-${randomUUID()}${UPDATE_SUFFIX}`
|
|
49909
49371
|
);
|
|
@@ -49941,7 +49403,7 @@ async function withWorkspaceLease(action, directory = updateRuntimeDirectory())
|
|
|
49941
49403
|
if (await updateLockIsActive(directory)) {
|
|
49942
49404
|
throw new Error("Glossa is updating. Run this workspace again after the update finishes.");
|
|
49943
49405
|
}
|
|
49944
|
-
const leaseFile =
|
|
49406
|
+
const leaseFile = path4.join(
|
|
49945
49407
|
directory,
|
|
49946
49408
|
`${process.pid}-${randomUUID()}${SESSION_SUFFIX}`
|
|
49947
49409
|
);
|
|
@@ -49968,10 +49430,10 @@ var DEVICE_PAIRING_LOCK_POLL_MS = 100;
|
|
|
49968
49430
|
var DEVICE_PAIRING_LOCK_MAX_AGE_MS = 6 * 6e4;
|
|
49969
49431
|
var DEVICE_PAIRING_GUARD_MAX_AGE_MS = 3e4;
|
|
49970
49432
|
function lockPath(directory) {
|
|
49971
|
-
return
|
|
49433
|
+
return path5.join(directory, DEVICE_PAIRING_LOCK);
|
|
49972
49434
|
}
|
|
49973
49435
|
function guardPath(directory) {
|
|
49974
|
-
return
|
|
49436
|
+
return path5.join(directory, DEVICE_PAIRING_LOCK_GUARD);
|
|
49975
49437
|
}
|
|
49976
49438
|
function newOwner() {
|
|
49977
49439
|
return {
|
|
@@ -50026,7 +49488,7 @@ async function acquireGuard(directory, signal) {
|
|
|
50026
49488
|
await rm3(file2, { force: true });
|
|
50027
49489
|
continue;
|
|
50028
49490
|
}
|
|
50029
|
-
await
|
|
49491
|
+
await delay(
|
|
50030
49492
|
DEVICE_PAIRING_LOCK_POLL_MS,
|
|
50031
49493
|
void 0,
|
|
50032
49494
|
signal ? { signal } : void 0
|
|
@@ -50074,7 +49536,7 @@ async function acquireDevicePairingLock(directory, signal) {
|
|
|
50074
49536
|
signal
|
|
50075
49537
|
);
|
|
50076
49538
|
if (reclaimed) continue;
|
|
50077
|
-
await
|
|
49539
|
+
await delay(
|
|
50078
49540
|
DEVICE_PAIRING_LOCK_POLL_MS,
|
|
50079
49541
|
void 0,
|
|
50080
49542
|
signal ? { signal } : void 0
|
|
@@ -50091,46 +49553,69 @@ async function withDevicePairingLease(action, signal, directory = updateRuntimeD
|
|
|
50091
49553
|
}
|
|
50092
49554
|
|
|
50093
49555
|
// src/device-pairing.ts
|
|
50094
|
-
|
|
49556
|
+
import { setTimeout as delay2 } from "node:timers/promises";
|
|
49557
|
+
var MAX_PAIRING_CODES = 3;
|
|
49558
|
+
var POLL_INTERVAL_MS = 2e3;
|
|
49559
|
+
function canceledError() {
|
|
50095
49560
|
return new Error("Pairing canceled.");
|
|
50096
49561
|
}
|
|
50097
|
-
function
|
|
50098
|
-
|
|
49562
|
+
async function defaultDelay(milliseconds, signal) {
|
|
49563
|
+
await delay2(milliseconds, void 0, signal ? { signal } : void 0);
|
|
50099
49564
|
}
|
|
50100
49565
|
async function pairDevice(endpoints, signal, dependencies = {}) {
|
|
50101
|
-
const
|
|
50102
|
-
const
|
|
50103
|
-
const authConfig = dependencies.loadAuthConfig ?? loadAuthConfig;
|
|
49566
|
+
const create3 = dependencies.createPairing ?? createPairing;
|
|
49567
|
+
const redeem = dependencies.redeemPairing ?? redeemPairing;
|
|
50104
49568
|
const name = dependencies.defaultDeviceName ?? defaultDeviceName;
|
|
49569
|
+
const wait = dependencies.delay ?? defaultDelay;
|
|
49570
|
+
const now = dependencies.now ?? Date.now;
|
|
50105
49571
|
const log = dependencies.log ?? console.log;
|
|
50106
49572
|
const baseFetch = dependencies.fetch ?? fetch;
|
|
50107
49573
|
const fetchRequest = signal ? async (input, init) => await baseFetch(input, { ...init, signal }) : baseFetch;
|
|
50108
49574
|
try {
|
|
50109
49575
|
signal?.throwIfAborted();
|
|
50110
49576
|
log("This computer needs to be paired with Glossa.");
|
|
50111
|
-
const
|
|
50112
|
-
const
|
|
50113
|
-
|
|
50114
|
-
|
|
50115
|
-
|
|
50116
|
-
|
|
50117
|
-
}
|
|
50118
|
-
|
|
50119
|
-
|
|
50120
|
-
|
|
50121
|
-
|
|
50122
|
-
|
|
50123
|
-
|
|
49577
|
+
const deviceName = name();
|
|
49578
|
+
const platform2 = `${process.platform}-${process.arch}`;
|
|
49579
|
+
for (let attempt = 0; attempt < MAX_PAIRING_CODES; attempt += 1) {
|
|
49580
|
+
signal?.throwIfAborted();
|
|
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
|
+
}
|
|
49607
|
+
}
|
|
49608
|
+
throw new Error("Pairing timed out. Run Glossa again to retry.");
|
|
50124
49609
|
} catch (error46) {
|
|
50125
49610
|
if (signal?.aborted || error46 instanceof Error && error46.name === "AbortError") {
|
|
50126
|
-
throw
|
|
49611
|
+
throw canceledError();
|
|
50127
49612
|
}
|
|
50128
49613
|
throw error46;
|
|
50129
49614
|
}
|
|
50130
49615
|
}
|
|
50131
49616
|
|
|
50132
49617
|
// src/worker/command-service.ts
|
|
50133
|
-
import { spawn
|
|
49618
|
+
import { spawn } from "node:child_process";
|
|
50134
49619
|
import { randomUUID as randomUUID3 } from "node:crypto";
|
|
50135
49620
|
import { StringDecoder } from "node:string_decoder";
|
|
50136
49621
|
import { setTimeout as delay3 } from "node:timers/promises";
|
|
@@ -50405,7 +49890,7 @@ async function terminateProcessTree(child) {
|
|
|
50405
49890
|
if (!child.pid || child.exitCode !== null) return;
|
|
50406
49891
|
if (process.platform === "win32") {
|
|
50407
49892
|
await new Promise((resolve) => {
|
|
50408
|
-
const killer =
|
|
49893
|
+
const killer = spawn("taskkill", ["/pid", String(child.pid), "/t", "/f"], {
|
|
50409
49894
|
stdio: "ignore",
|
|
50410
49895
|
windowsHide: true
|
|
50411
49896
|
});
|
|
@@ -50495,7 +49980,7 @@ var CommandService = class {
|
|
|
50495
49980
|
const invocation = options.argv ? { file: options.argv[0], args: options.argv.slice(1) } : shellInvocation(options.shellCommand);
|
|
50496
49981
|
let child;
|
|
50497
49982
|
try {
|
|
50498
|
-
child =
|
|
49983
|
+
child = spawn(invocation.file, invocation.args, {
|
|
50499
49984
|
cwd: cwd2,
|
|
50500
49985
|
env: process.env,
|
|
50501
49986
|
detached: process.platform !== "win32",
|
|
@@ -50727,14 +50212,14 @@ import {
|
|
|
50727
50212
|
stat as stat3,
|
|
50728
50213
|
writeFile as writeFile5
|
|
50729
50214
|
} from "node:fs/promises";
|
|
50730
|
-
import
|
|
50215
|
+
import path7 from "node:path";
|
|
50731
50216
|
import { performance as performance2 } from "node:perf_hooks";
|
|
50732
50217
|
import { StringDecoder as StringDecoder2 } from "node:string_decoder";
|
|
50733
50218
|
|
|
50734
50219
|
// src/worker/path-policy.ts
|
|
50735
50220
|
import { lstat, realpath, stat as stat2 } from "node:fs/promises";
|
|
50736
50221
|
import os5 from "node:os";
|
|
50737
|
-
import
|
|
50222
|
+
import path6 from "node:path";
|
|
50738
50223
|
function samePath(left, right) {
|
|
50739
50224
|
return process.platform === "win32" ? left.toLowerCase() === right.toLowerCase() : left === right;
|
|
50740
50225
|
}
|
|
@@ -50746,15 +50231,15 @@ function accountHomeDirectory() {
|
|
|
50746
50231
|
}
|
|
50747
50232
|
}
|
|
50748
50233
|
function isWithin(root, candidate) {
|
|
50749
|
-
const relative =
|
|
50750
|
-
return relative === "" || !relative.startsWith(`..${
|
|
50234
|
+
const relative = path6.relative(root, candidate);
|
|
50235
|
+
return relative === "" || !relative.startsWith(`..${path6.sep}`) && relative !== ".." && !path6.isAbsolute(relative);
|
|
50751
50236
|
}
|
|
50752
50237
|
function validateRelativePath(value) {
|
|
50753
50238
|
if (value.includes("\0")) {
|
|
50754
50239
|
throw new WorkerError("invalid_path", "Paths cannot contain null bytes.");
|
|
50755
50240
|
}
|
|
50756
50241
|
const explicitNativePosixPath = process.platform !== "win32" && value.startsWith("./");
|
|
50757
|
-
if (
|
|
50242
|
+
if (path6.isAbsolute(value) || path6.posix.isAbsolute(value) || !explicitNativePosixPath && path6.win32.isAbsolute(value)) {
|
|
50758
50243
|
throw new WorkerError("absolute_path", "Absolute paths are not allowed.");
|
|
50759
50244
|
}
|
|
50760
50245
|
const segments = explicitNativePosixPath ? value.split(/\/+/).filter(Boolean) : value.split(/[\\/]+/);
|
|
@@ -50764,7 +50249,7 @@ function validateRelativePath(value) {
|
|
|
50764
50249
|
return value === "" ? "." : value;
|
|
50765
50250
|
}
|
|
50766
50251
|
async function canonicalizeRoot(candidate) {
|
|
50767
|
-
const root = await realpath(
|
|
50252
|
+
const root = await realpath(path6.resolve(candidate)).catch((error46) => {
|
|
50768
50253
|
if (error46.code === "ENOENT") {
|
|
50769
50254
|
throw new WorkerError("root_not_found", "The workspace directory does not exist.");
|
|
50770
50255
|
}
|
|
@@ -50774,10 +50259,10 @@ async function canonicalizeRoot(candidate) {
|
|
|
50774
50259
|
if (!rootStat.isDirectory()) {
|
|
50775
50260
|
throw new WorkerError("root_not_directory", "The exposed root must be a directory.");
|
|
50776
50261
|
}
|
|
50777
|
-
const filesystemRoot =
|
|
50262
|
+
const filesystemRoot = path6.parse(root).root;
|
|
50778
50263
|
const homes = await Promise.all(
|
|
50779
50264
|
[os5.homedir(), accountHomeDirectory()].map(
|
|
50780
|
-
async (home) => await realpath(home).catch(() =>
|
|
50265
|
+
async (home) => await realpath(home).catch(() => path6.resolve(home))
|
|
50781
50266
|
)
|
|
50782
50267
|
);
|
|
50783
50268
|
const isHomeDirectory = homes.some((home) => samePath(root, home));
|
|
@@ -50813,7 +50298,7 @@ var PathPolicy = class _PathPolicy {
|
|
|
50813
50298
|
return canonical;
|
|
50814
50299
|
}
|
|
50815
50300
|
async resolveDiscoveredExisting(candidate) {
|
|
50816
|
-
const lexical =
|
|
50301
|
+
const lexical = path6.resolve(candidate);
|
|
50817
50302
|
if (!isWithin(this.root, lexical)) {
|
|
50818
50303
|
throw new WorkerError("path_escape", "The requested path escapes the exposed root.");
|
|
50819
50304
|
}
|
|
@@ -50831,7 +50316,7 @@ var PathPolicy = class _PathPolicy {
|
|
|
50831
50316
|
}
|
|
50832
50317
|
async resolveWritableFile(relativePath) {
|
|
50833
50318
|
const lexical = this.resolveLexical(relativePath);
|
|
50834
|
-
const parent =
|
|
50319
|
+
const parent = path6.dirname(lexical);
|
|
50835
50320
|
await this.rejectLinkedComponents(parent);
|
|
50836
50321
|
const canonicalParent = await realpath(parent).catch((error46) => {
|
|
50837
50322
|
if (error46.code === "ENOENT") {
|
|
@@ -50862,7 +50347,7 @@ var PathPolicy = class _PathPolicy {
|
|
|
50862
50347
|
if (error46 instanceof WorkerError) throw error46;
|
|
50863
50348
|
if (error46.code !== "ENOENT") throw error46;
|
|
50864
50349
|
}
|
|
50865
|
-
return
|
|
50350
|
+
return path6.join(canonicalParent, path6.basename(lexical));
|
|
50866
50351
|
}
|
|
50867
50352
|
async resolveWritableDirectory(relativePath, recursive) {
|
|
50868
50353
|
const lexical = this.resolveLexical(relativePath);
|
|
@@ -50885,7 +50370,7 @@ var PathPolicy = class _PathPolicy {
|
|
|
50885
50370
|
if (error46.code !== "ENOENT") throw error46;
|
|
50886
50371
|
}
|
|
50887
50372
|
if (!recursive) {
|
|
50888
|
-
const parent =
|
|
50373
|
+
const parent = path6.dirname(lexical);
|
|
50889
50374
|
await this.rejectLinkedComponents(parent);
|
|
50890
50375
|
const canonicalParent = await realpath(parent).catch(
|
|
50891
50376
|
(error46) => {
|
|
@@ -50905,11 +50390,11 @@ var PathPolicy = class _PathPolicy {
|
|
|
50905
50390
|
throw new WorkerError("not_directory", "The destination parent is not a directory.");
|
|
50906
50391
|
}
|
|
50907
50392
|
return {
|
|
50908
|
-
target:
|
|
50393
|
+
target: path6.join(canonicalParent, path6.basename(lexical)),
|
|
50909
50394
|
exists: false
|
|
50910
50395
|
};
|
|
50911
50396
|
}
|
|
50912
|
-
let existingAncestor =
|
|
50397
|
+
let existingAncestor = path6.dirname(lexical);
|
|
50913
50398
|
while (!samePath(existingAncestor, this.root)) {
|
|
50914
50399
|
try {
|
|
50915
50400
|
await this.rejectLinkedComponents(existingAncestor);
|
|
@@ -50921,9 +50406,9 @@ var PathPolicy = class _PathPolicy {
|
|
|
50921
50406
|
throw new WorkerError("not_directory", "The destination parent is not a directory.");
|
|
50922
50407
|
}
|
|
50923
50408
|
return {
|
|
50924
|
-
target:
|
|
50409
|
+
target: path6.join(
|
|
50925
50410
|
canonicalAncestor,
|
|
50926
|
-
|
|
50411
|
+
path6.relative(existingAncestor, lexical)
|
|
50927
50412
|
),
|
|
50928
50413
|
exists: false
|
|
50929
50414
|
};
|
|
@@ -50931,13 +50416,13 @@ var PathPolicy = class _PathPolicy {
|
|
|
50931
50416
|
if (error46 instanceof WorkerError) throw error46;
|
|
50932
50417
|
if (error46.code !== "ENOENT") throw error46;
|
|
50933
50418
|
}
|
|
50934
|
-
existingAncestor =
|
|
50419
|
+
existingAncestor = path6.dirname(existingAncestor);
|
|
50935
50420
|
}
|
|
50936
50421
|
return { target: lexical, exists: false };
|
|
50937
50422
|
}
|
|
50938
50423
|
async resolveVacantPath(relativePath) {
|
|
50939
50424
|
const lexical = this.resolveLexical(relativePath);
|
|
50940
|
-
const parent =
|
|
50425
|
+
const parent = path6.dirname(lexical);
|
|
50941
50426
|
await this.rejectLinkedComponents(parent);
|
|
50942
50427
|
const canonicalParent = await realpath(parent).catch(
|
|
50943
50428
|
(error46) => {
|
|
@@ -50966,11 +50451,11 @@ var PathPolicy = class _PathPolicy {
|
|
|
50966
50451
|
if (error46 instanceof WorkerError) throw error46;
|
|
50967
50452
|
if (error46.code !== "ENOENT") throw error46;
|
|
50968
50453
|
}
|
|
50969
|
-
return
|
|
50454
|
+
return path6.join(canonicalParent, path6.basename(lexical));
|
|
50970
50455
|
}
|
|
50971
50456
|
resolveLexical(relativePath) {
|
|
50972
50457
|
const validated = validateRelativePath(relativePath);
|
|
50973
|
-
const candidate =
|
|
50458
|
+
const candidate = path6.resolve(this.root, validated);
|
|
50974
50459
|
if (!isWithin(this.root, candidate)) {
|
|
50975
50460
|
throw new WorkerError("path_escape", "The requested path escapes the exposed root.");
|
|
50976
50461
|
}
|
|
@@ -50980,11 +50465,11 @@ var PathPolicy = class _PathPolicy {
|
|
|
50980
50465
|
if (!isWithin(this.root, candidate)) {
|
|
50981
50466
|
throw new WorkerError("path_escape", "The requested path escapes the exposed root.");
|
|
50982
50467
|
}
|
|
50983
|
-
const relative =
|
|
50468
|
+
const relative = path6.relative(this.root, candidate);
|
|
50984
50469
|
if (!relative) return;
|
|
50985
50470
|
let current = this.root;
|
|
50986
|
-
for (const segment of relative.split(
|
|
50987
|
-
current =
|
|
50471
|
+
for (const segment of relative.split(path6.sep)) {
|
|
50472
|
+
current = path6.join(current, segment);
|
|
50988
50473
|
try {
|
|
50989
50474
|
const currentStat = await lstat(current);
|
|
50990
50475
|
if (currentStat.isSymbolicLink()) {
|
|
@@ -51007,12 +50492,12 @@ function sha256(content) {
|
|
|
51007
50492
|
return createHash("sha256").update(content).digest("hex");
|
|
51008
50493
|
}
|
|
51009
50494
|
function isPathWithin(root, candidate) {
|
|
51010
|
-
const relative =
|
|
51011
|
-
return relative === "" || !relative.startsWith(`..${
|
|
50495
|
+
const relative = path7.relative(root, candidate);
|
|
50496
|
+
return relative === "" || !relative.startsWith(`..${path7.sep}`) && relative !== ".." && !path7.isAbsolute(relative);
|
|
51012
50497
|
}
|
|
51013
50498
|
var fileWriteTails = /* @__PURE__ */ new Map();
|
|
51014
50499
|
async function withFileWriteLock(target, operation) {
|
|
51015
|
-
const normalized =
|
|
50500
|
+
const normalized = path7.normalize(target);
|
|
51016
50501
|
const key = process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
51017
50502
|
const predecessor = fileWriteTails.get(key);
|
|
51018
50503
|
let release;
|
|
@@ -51029,7 +50514,7 @@ async function withFileWriteLock(target, operation) {
|
|
|
51029
50514
|
}
|
|
51030
50515
|
}
|
|
51031
50516
|
async function withFileWriteLocks(targets, operation) {
|
|
51032
|
-
const unique = [...new Set(targets.map((target) =>
|
|
50517
|
+
const unique = [...new Set(targets.map((target) => path7.normalize(target)))].sort(
|
|
51033
50518
|
(left, right) => left.localeCompare(right)
|
|
51034
50519
|
);
|
|
51035
50520
|
const run = async (index) => {
|
|
@@ -51065,7 +50550,7 @@ function compareNames(left, right) {
|
|
|
51065
50550
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
51066
50551
|
}
|
|
51067
50552
|
function relativeSortKey(root, target) {
|
|
51068
|
-
const relative =
|
|
50553
|
+
const relative = path7.relative(root, target);
|
|
51069
50554
|
if (!relative) return ".";
|
|
51070
50555
|
return process.platform === "win32" ? relative.replaceAll("\\", "/") : relative;
|
|
51071
50556
|
}
|
|
@@ -51449,7 +50934,7 @@ var FileService = class {
|
|
|
51449
50934
|
if (batch.overflow) throw scanLimitError();
|
|
51450
50935
|
scannedEntries += batch.children.length;
|
|
51451
50936
|
for (const child of batch.children) {
|
|
51452
|
-
const target =
|
|
50937
|
+
const target = path7.join(directory, child.name);
|
|
51453
50938
|
heapPush(frontier, {
|
|
51454
50939
|
target,
|
|
51455
50940
|
sortKey: relativeSortKey(this.policy.root, target),
|
|
@@ -51473,7 +50958,7 @@ var FileService = class {
|
|
|
51473
50958
|
throw scanLimitError();
|
|
51474
50959
|
}
|
|
51475
50960
|
scannedEntries += 1;
|
|
51476
|
-
const target =
|
|
50961
|
+
const target = path7.join(directory, child.name);
|
|
51477
50962
|
let childStat;
|
|
51478
50963
|
try {
|
|
51479
50964
|
childStat = await this.#withinDeadline(
|
|
@@ -51625,7 +51110,7 @@ var FileService = class {
|
|
|
51625
51110
|
if (!patterns) return false;
|
|
51626
51111
|
const normalized = relativePath.replaceAll("\\", "/");
|
|
51627
51112
|
try {
|
|
51628
|
-
return patterns.some((pattern) =>
|
|
51113
|
+
return patterns.some((pattern) => path7.posix.matchesGlob(normalized, pattern));
|
|
51629
51114
|
} catch {
|
|
51630
51115
|
throw new WorkerError("invalid_search", "Search glob pattern is invalid.");
|
|
51631
51116
|
}
|
|
@@ -51654,7 +51139,7 @@ var FileService = class {
|
|
|
51654
51139
|
const searchFile = async (target) => {
|
|
51655
51140
|
const relative = displayPath(this.policy.root, target);
|
|
51656
51141
|
if (extensions && !extensions.some(
|
|
51657
|
-
(extension) =>
|
|
51142
|
+
(extension) => path7.basename(target).toLowerCase().endsWith(extension)
|
|
51658
51143
|
)) {
|
|
51659
51144
|
return false;
|
|
51660
51145
|
}
|
|
@@ -51749,7 +51234,7 @@ var FileService = class {
|
|
|
51749
51234
|
scannedEntries += batch.children.length;
|
|
51750
51235
|
for (const child of batch.children) {
|
|
51751
51236
|
this.#assertBeforeDeadline(deadlineAt);
|
|
51752
|
-
const target =
|
|
51237
|
+
const target = path7.join(directory, child.name);
|
|
51753
51238
|
let targetType;
|
|
51754
51239
|
if (this.#trustDirectoryEntryTypes) {
|
|
51755
51240
|
if (child.isSymbolicLink()) {
|
|
@@ -52059,7 +51544,7 @@ var FileService = class {
|
|
|
52059
51544
|
);
|
|
52060
51545
|
}
|
|
52061
51546
|
if (expectedSha256) await requireRevision(target, expectedSha256);
|
|
52062
|
-
const temporary =
|
|
51547
|
+
const temporary = path7.join(path7.dirname(target), `.glossa-${randomUUID4()}.tmp`);
|
|
52063
51548
|
try {
|
|
52064
51549
|
await writeFile5(temporary, bytes, { flag: "wx", mode: 384 });
|
|
52065
51550
|
target = await this.policy.resolveWritableFile(relativePath);
|
|
@@ -52307,18 +51792,25 @@ var DEFAULT_HEARTBEAT_MS = 15e3;
|
|
|
52307
51792
|
var CAPACITY_REFRESH_POLL_MS = 1;
|
|
52308
51793
|
var MAX_CONCURRENT_JOBS = 5;
|
|
52309
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
|
+
};
|
|
52310
51802
|
var DeviceRejectedError = class extends Error {
|
|
52311
51803
|
constructor() {
|
|
52312
51804
|
super("The relay rejected the device credential.");
|
|
52313
51805
|
this.name = "DeviceRejectedError";
|
|
52314
51806
|
}
|
|
52315
51807
|
};
|
|
52316
|
-
var
|
|
52317
|
-
constructor(
|
|
51808
|
+
var RelayProtocolUnsupportedError = class extends Error {
|
|
51809
|
+
constructor() {
|
|
52318
51810
|
super(
|
|
52319
|
-
|
|
51811
|
+
"The relay does not support this Glossa worker protocol. Update the relay before reconnecting this workspace."
|
|
52320
51812
|
);
|
|
52321
|
-
this.name = "
|
|
51813
|
+
this.name = "RelayProtocolUnsupportedError";
|
|
52322
51814
|
}
|
|
52323
51815
|
};
|
|
52324
51816
|
var RelayResponseError = class extends Error {
|
|
@@ -52329,19 +51821,20 @@ var RelayResponseError = class extends Error {
|
|
|
52329
51821
|
}
|
|
52330
51822
|
status;
|
|
52331
51823
|
};
|
|
52332
|
-
function
|
|
52333
|
-
if (value === void 0) return void 0;
|
|
51824
|
+
function requiredWorkerToken(value) {
|
|
52334
51825
|
if (typeof value !== "string" || !WORKER_TOKEN_PATTERN.test(value)) {
|
|
52335
51826
|
throw new Error("The relay returned an invalid worker credential.");
|
|
52336
51827
|
}
|
|
52337
51828
|
return value;
|
|
52338
51829
|
}
|
|
52339
|
-
function
|
|
51830
|
+
function acceptsCurrentCapabilities(value) {
|
|
52340
51831
|
if (typeof value !== "object" || value === null) return false;
|
|
52341
51832
|
if (!("capabilities" in value)) return false;
|
|
52342
51833
|
const capabilities = value.capabilities;
|
|
52343
51834
|
if (typeof capabilities !== "object" || capabilities === null) return false;
|
|
52344
|
-
return
|
|
51835
|
+
return Object.keys(WORKER_CAPABILITIES).every(
|
|
51836
|
+
(capability) => capabilities[capability] === true
|
|
51837
|
+
);
|
|
52345
51838
|
}
|
|
52346
51839
|
function jobLane(job) {
|
|
52347
51840
|
switch (job.type) {
|
|
@@ -52364,25 +51857,25 @@ function jobLane(job) {
|
|
|
52364
51857
|
return "mutation";
|
|
52365
51858
|
}
|
|
52366
51859
|
}
|
|
52367
|
-
function acceptedJobTypes(counts, total
|
|
51860
|
+
function acceptedJobTypes(counts, total) {
|
|
52368
51861
|
if (total >= MAX_CONCURRENT_JOBS) return [];
|
|
52369
51862
|
const accepted = [];
|
|
52370
51863
|
if (counts.status < 1) {
|
|
52371
|
-
accepted.push("get_command");
|
|
52372
|
-
if (commandOutputRanges) accepted.push("read_command_output");
|
|
51864
|
+
accepted.push("get_command", "read_command_output");
|
|
52373
51865
|
}
|
|
52374
51866
|
if (counts.cancel < 1) accepted.push("cancel_command");
|
|
52375
51867
|
if (counts.read < 2) {
|
|
52376
|
-
accepted.push("read_file");
|
|
52377
|
-
if (structuredReads) {
|
|
52378
|
-
accepted.push("list_files", "search_text", "read_file_range");
|
|
52379
|
-
}
|
|
51868
|
+
accepted.push("read_file", "list_files", "search_text", "read_file_range");
|
|
52380
51869
|
}
|
|
52381
51870
|
if (counts.mutation < 1) {
|
|
52382
|
-
accepted.push(
|
|
52383
|
-
|
|
52384
|
-
|
|
52385
|
-
|
|
51871
|
+
accepted.push(
|
|
51872
|
+
"write_file",
|
|
51873
|
+
"edit_file",
|
|
51874
|
+
"make_directory",
|
|
51875
|
+
"delete_path",
|
|
51876
|
+
"move_path",
|
|
51877
|
+
"run_command"
|
|
51878
|
+
);
|
|
52386
51879
|
}
|
|
52387
51880
|
return accepted;
|
|
52388
51881
|
}
|
|
@@ -52424,7 +51917,7 @@ var RemoteWorker = class {
|
|
|
52424
51917
|
constructor(options) {
|
|
52425
51918
|
this.#origin = new URL(options.origin);
|
|
52426
51919
|
this.#deviceToken = options.deviceToken;
|
|
52427
|
-
this.#accessProfile = options.accessProfile;
|
|
51920
|
+
this.#accessProfile = options.accessProfile ?? "workspace";
|
|
52428
51921
|
this.#workspaceLabel = options.workspaceLabel;
|
|
52429
51922
|
this.#workerVersion = options.workerVersion;
|
|
52430
51923
|
this.#worker = options.worker;
|
|
@@ -52450,17 +51943,14 @@ var RemoteWorker = class {
|
|
|
52450
51943
|
registeredSession = session;
|
|
52451
51944
|
this.#onStatus({
|
|
52452
51945
|
state: "connected",
|
|
52453
|
-
reconnected: connectedBefore
|
|
52454
|
-
legacyRelay: session.legacyRelay,
|
|
52455
|
-
accessProfileAccepted: session.accessProfileAccepted,
|
|
52456
|
-
workspaceLabelAccepted: session.workspaceLabelAccepted
|
|
51946
|
+
reconnected: connectedBefore
|
|
52457
51947
|
});
|
|
52458
51948
|
connectedBefore = true;
|
|
52459
51949
|
failures = 0;
|
|
52460
51950
|
await this.#pollGeneration(session);
|
|
52461
51951
|
} catch (error46) {
|
|
52462
51952
|
if (this.#signal.aborted) return;
|
|
52463
|
-
if (error46 instanceof DeviceRejectedError || error46 instanceof
|
|
51953
|
+
if (error46 instanceof DeviceRejectedError || error46 instanceof RelayProtocolUnsupportedError) {
|
|
52464
51954
|
throw error46;
|
|
52465
51955
|
}
|
|
52466
51956
|
const delay5 = reconnectDelayMs(
|
|
@@ -52489,153 +51979,33 @@ var RemoteWorker = class {
|
|
|
52489
51979
|
}
|
|
52490
51980
|
}
|
|
52491
51981
|
async #register() {
|
|
52492
|
-
|
|
52493
|
-
|
|
52494
|
-
|
|
52495
|
-
commandProgress: true,
|
|
52496
|
-
concurrentJobs: true,
|
|
52497
|
-
structuredReads: true,
|
|
52498
|
-
structuredMutations: true,
|
|
52499
|
-
commandOutputRanges: true
|
|
52500
|
-
}
|
|
52501
|
-
};
|
|
52502
|
-
const mutationBody = {
|
|
52503
|
-
workerId: this.#workerId,
|
|
52504
|
-
capabilities: {
|
|
52505
|
-
commandProgress: true,
|
|
52506
|
-
concurrentJobs: true,
|
|
52507
|
-
structuredReads: true,
|
|
52508
|
-
structuredMutations: true
|
|
52509
|
-
}
|
|
52510
|
-
};
|
|
52511
|
-
const structuredBody = {
|
|
52512
|
-
workerId: this.#workerId,
|
|
52513
|
-
capabilities: {
|
|
52514
|
-
commandProgress: true,
|
|
52515
|
-
concurrentJobs: true,
|
|
52516
|
-
structuredReads: true
|
|
52517
|
-
}
|
|
52518
|
-
};
|
|
52519
|
-
const concurrentBody = {
|
|
52520
|
-
workerId: this.#workerId,
|
|
52521
|
-
capabilities: { commandProgress: true, concurrentJobs: true }
|
|
52522
|
-
};
|
|
52523
|
-
const capabilityBodies = [
|
|
52524
|
-
currentBody,
|
|
52525
|
-
mutationBody,
|
|
52526
|
-
structuredBody,
|
|
52527
|
-
concurrentBody,
|
|
52528
|
-
{
|
|
51982
|
+
let response;
|
|
51983
|
+
try {
|
|
51984
|
+
response = await this.#post("/device/register", {
|
|
52529
51985
|
workerId: this.#workerId,
|
|
52530
|
-
|
|
52531
|
-
|
|
52532
|
-
|
|
52533
|
-
|
|
52534
|
-
|
|
52535
|
-
|
|
52536
|
-
|
|
52537
|
-
|
|
52538
|
-
},
|
|
52539
|
-
...this.#workspaceLabel ? [{
|
|
52540
|
-
includeVersion: Boolean(this.#workerVersion),
|
|
52541
|
-
includeLabel: false
|
|
52542
|
-
}] : [],
|
|
52543
|
-
...this.#workerVersion && this.#workspaceLabel ? [{ includeVersion: false, includeLabel: true }] : [],
|
|
52544
|
-
{ includeVersion: false, includeLabel: false }
|
|
52545
|
-
];
|
|
52546
|
-
const attempts = [];
|
|
52547
|
-
const seenAttempts = /* @__PURE__ */ new Set();
|
|
52548
|
-
const pushAttempt = (base, includeProfile, includeVersion, includeLabel, legacyRelay = false) => {
|
|
52549
|
-
const body = {
|
|
52550
|
-
...base,
|
|
52551
|
-
...includeProfile && this.#accessProfile ? { accessProfile: this.#accessProfile } : {},
|
|
52552
|
-
...includeVersion && this.#workerVersion ? { workerVersion: this.#workerVersion } : {},
|
|
52553
|
-
...includeLabel && this.#workspaceLabel ? { workspaceLabel: this.#workspaceLabel } : {}
|
|
52554
|
-
};
|
|
52555
|
-
const key = JSON.stringify(body);
|
|
52556
|
-
if (seenAttempts.has(key)) return;
|
|
52557
|
-
seenAttempts.add(key);
|
|
52558
|
-
attempts.push({ body, legacyRelay });
|
|
52559
|
-
};
|
|
52560
|
-
if (this.#accessProfile) {
|
|
52561
|
-
for (const metadata of metadataPhases) {
|
|
52562
|
-
for (const body of capabilityBodies) {
|
|
52563
|
-
pushAttempt(
|
|
52564
|
-
body,
|
|
52565
|
-
true,
|
|
52566
|
-
metadata.includeVersion,
|
|
52567
|
-
metadata.includeLabel
|
|
52568
|
-
);
|
|
52569
|
-
}
|
|
52570
|
-
}
|
|
52571
|
-
}
|
|
52572
|
-
if (this.#accessProfile === void 0 || this.#accessProfile === "system") {
|
|
52573
|
-
for (const metadata of metadataPhases) {
|
|
52574
|
-
for (const body of capabilityBodies) {
|
|
52575
|
-
pushAttempt(
|
|
52576
|
-
body,
|
|
52577
|
-
false,
|
|
52578
|
-
metadata.includeVersion,
|
|
52579
|
-
metadata.includeLabel
|
|
52580
|
-
);
|
|
52581
|
-
}
|
|
52582
|
-
}
|
|
52583
|
-
attempts.push({ body: {}, legacyRelay: true });
|
|
52584
|
-
}
|
|
52585
|
-
for (const attempt of attempts) {
|
|
52586
|
-
let response;
|
|
52587
|
-
try {
|
|
52588
|
-
response = await this.#post("/device/register", attempt.body);
|
|
52589
|
-
} catch (error46) {
|
|
52590
|
-
if (error46 instanceof RelayResponseError && error46.status === 400) {
|
|
52591
|
-
continue;
|
|
52592
|
-
}
|
|
52593
|
-
throw error46;
|
|
52594
|
-
}
|
|
52595
|
-
const value = await response.json();
|
|
52596
|
-
if (typeof value !== "object" || value === null || !("generation" in value) || typeof value.generation !== "string") {
|
|
52597
|
-
throw new Error("The relay returned an invalid registration response.");
|
|
52598
|
-
}
|
|
52599
|
-
if (!attempt.legacyRelay && (!("workerId" in value) || value.workerId !== this.#workerId)) {
|
|
52600
|
-
throw new Error("The relay returned an invalid registration response.");
|
|
52601
|
-
}
|
|
52602
|
-
const workerToken = "workerToken" in value ? optionalWorkerToken(value.workerToken) : void 0;
|
|
52603
|
-
const accessProfileAccepted = this.#accessProfile === void 0 || this.#accessProfile === "system" || "accessProfile" in value && value.accessProfile === this.#accessProfile;
|
|
52604
|
-
const session = {
|
|
52605
|
-
generation: value.generation,
|
|
52606
|
-
legacyRelay: attempt.legacyRelay,
|
|
52607
|
-
concurrentJobs: !attempt.legacyRelay && supportsCapability(value, "concurrentJobs"),
|
|
52608
|
-
structuredReads: !attempt.legacyRelay && supportsCapability(value, "structuredReads"),
|
|
52609
|
-
structuredMutations: !attempt.legacyRelay && supportsCapability(value, "structuredMutations"),
|
|
52610
|
-
commandOutputRanges: !attempt.legacyRelay && supportsCapability(value, "commandOutputRanges"),
|
|
52611
|
-
accessProfileAccepted,
|
|
52612
|
-
workspaceLabelAccepted: this.#workspaceLabel === void 0 || "workspaceLabel" in value && value.workspaceLabel === this.#workspaceLabel,
|
|
52613
|
-
...workerToken ? { workerToken } : {}
|
|
52614
|
-
};
|
|
52615
|
-
if (!accessProfileAccepted && this.#accessProfile) {
|
|
52616
|
-
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();
|
|
52617
51994
|
}
|
|
52618
|
-
|
|
51995
|
+
throw error46;
|
|
52619
51996
|
}
|
|
52620
|
-
|
|
52621
|
-
|
|
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();
|
|
52622
52000
|
}
|
|
52623
|
-
|
|
52001
|
+
return {
|
|
52002
|
+
generation: value.generation,
|
|
52003
|
+
workerToken: requiredWorkerToken(value.workerToken)
|
|
52004
|
+
};
|
|
52624
52005
|
}
|
|
52625
52006
|
async #pollGeneration(session) {
|
|
52626
|
-
if (session.legacyRelay || !session.concurrentJobs) {
|
|
52627
|
-
await this.#pollSequentially(session);
|
|
52628
|
-
return;
|
|
52629
|
-
}
|
|
52630
52007
|
await this.#pollConcurrently(session);
|
|
52631
52008
|
}
|
|
52632
|
-
async #pollSequentially(session) {
|
|
52633
|
-
while (!this.#signal.aborted) {
|
|
52634
|
-
const job = await this.#pollForJob(session);
|
|
52635
|
-
if (!job) continue;
|
|
52636
|
-
await this.#handleAndPost(session, job, true);
|
|
52637
|
-
}
|
|
52638
|
-
}
|
|
52639
52009
|
async #pollConcurrently(session) {
|
|
52640
52010
|
const counts = {
|
|
52641
52011
|
status: 0,
|
|
@@ -52693,7 +52063,7 @@ var RemoteWorker = class {
|
|
|
52693
52063
|
counts[lane] += 1;
|
|
52694
52064
|
ensureHeartbeat();
|
|
52695
52065
|
let task;
|
|
52696
|
-
task = this.#handleAndPost(session, job
|
|
52066
|
+
task = this.#handleAndPost(session, job).catch((error46) => {
|
|
52697
52067
|
failure ??= error46;
|
|
52698
52068
|
}).finally(() => {
|
|
52699
52069
|
counts[lane] -= 1;
|
|
@@ -52706,13 +52076,7 @@ var RemoteWorker = class {
|
|
|
52706
52076
|
try {
|
|
52707
52077
|
while (!this.#signal.aborted) {
|
|
52708
52078
|
if (failure !== void 0) throw failure;
|
|
52709
|
-
const acceptedTypes = acceptedJobTypes(
|
|
52710
|
-
counts,
|
|
52711
|
-
inFlight.size,
|
|
52712
|
-
session.structuredReads,
|
|
52713
|
-
session.structuredMutations,
|
|
52714
|
-
session.commandOutputRanges
|
|
52715
|
-
);
|
|
52079
|
+
const acceptedTypes = acceptedJobTypes(counts, inFlight.size);
|
|
52716
52080
|
if (acceptedTypes.length === 0) {
|
|
52717
52081
|
await Promise.race(inFlight);
|
|
52718
52082
|
continue;
|
|
@@ -52738,13 +52102,7 @@ var RemoteWorker = class {
|
|
|
52738
52102
|
}
|
|
52739
52103
|
if (failure !== void 0) throw failure;
|
|
52740
52104
|
observedCapacityVersion = capacityVersion;
|
|
52741
|
-
const refreshedTypes = acceptedJobTypes(
|
|
52742
|
-
counts,
|
|
52743
|
-
inFlight.size,
|
|
52744
|
-
session.structuredReads,
|
|
52745
|
-
session.structuredMutations,
|
|
52746
|
-
session.commandOutputRanges
|
|
52747
|
-
);
|
|
52105
|
+
const refreshedTypes = acceptedJobTypes(counts, inFlight.size);
|
|
52748
52106
|
const newlyAcceptedTypes = refreshedTypes.filter(
|
|
52749
52107
|
(type) => !acceptedTypes.includes(type)
|
|
52750
52108
|
);
|
|
@@ -52783,7 +52141,7 @@ var RemoteWorker = class {
|
|
|
52783
52141
|
async #pollForJob(session, acceptedTypes, waitMs) {
|
|
52784
52142
|
const response = await this.#post(
|
|
52785
52143
|
"/device/poll",
|
|
52786
|
-
|
|
52144
|
+
{
|
|
52787
52145
|
workerId: this.#workerId,
|
|
52788
52146
|
generation: session.generation,
|
|
52789
52147
|
...acceptedTypes ? { acceptedTypes } : {},
|
|
@@ -52801,26 +52159,12 @@ var RemoteWorker = class {
|
|
|
52801
52159
|
}
|
|
52802
52160
|
return parsed.data;
|
|
52803
52161
|
}
|
|
52804
|
-
async #handleAndPost(session, job
|
|
52805
|
-
const
|
|
52806
|
-
void this.#post(
|
|
52807
|
-
"/device/heartbeat",
|
|
52808
|
-
{ workerId: this.#workerId, generation: session.generation },
|
|
52809
|
-
session.workerToken
|
|
52810
|
-
).catch(() => {
|
|
52811
|
-
});
|
|
52812
|
-
}, this.#heartbeatMs) : void 0;
|
|
52813
|
-
heartbeat?.unref();
|
|
52814
|
-
let result;
|
|
52815
|
-
try {
|
|
52816
|
-
result = await this.#worker.handle(job);
|
|
52817
|
-
} finally {
|
|
52818
|
-
if (heartbeat) clearInterval(heartbeat);
|
|
52819
|
-
}
|
|
52162
|
+
async #handleAndPost(session, job) {
|
|
52163
|
+
const result = await this.#worker.handle(job);
|
|
52820
52164
|
try {
|
|
52821
52165
|
await this.#post(
|
|
52822
52166
|
"/device/result",
|
|
52823
|
-
|
|
52167
|
+
{ workerId: this.#workerId, result },
|
|
52824
52168
|
session.workerToken
|
|
52825
52169
|
);
|
|
52826
52170
|
} catch (error46) {
|
|
@@ -52829,39 +52173,24 @@ var RemoteWorker = class {
|
|
|
52829
52173
|
}
|
|
52830
52174
|
}
|
|
52831
52175
|
async #unregister(session) {
|
|
52832
|
-
|
|
52833
|
-
|
|
52834
|
-
|
|
52835
|
-
|
|
52836
|
-
|
|
52837
|
-
|
|
52838
|
-
|
|
52839
|
-
|
|
52840
|
-
|
|
52841
|
-
|
|
52842
|
-
|
|
52843
|
-
|
|
52844
|
-
}
|
|
52845
|
-
);
|
|
52846
|
-
if (response.ok) return "accepted";
|
|
52847
|
-
return [400, 401, 404, 409].includes(response.status) ? "rejected" : "unreachable";
|
|
52848
|
-
} catch {
|
|
52849
|
-
return "unreachable";
|
|
52850
|
-
}
|
|
52851
|
-
};
|
|
52852
|
-
if (!session?.workerToken) {
|
|
52853
|
-
await unregister(`Device ${this.#deviceToken}`);
|
|
52854
|
-
return;
|
|
52855
|
-
}
|
|
52856
|
-
const result = await unregister(`Worker ${session.workerToken}`);
|
|
52857
|
-
if (result === "rejected") {
|
|
52858
|
-
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 {
|
|
52859
52188
|
}
|
|
52860
52189
|
}
|
|
52861
|
-
async #post(
|
|
52190
|
+
async #post(path12, body, workerToken) {
|
|
52862
52191
|
const timeout = AbortSignal.timeout(WORKER_REQUEST_TIMEOUT_MS);
|
|
52863
52192
|
const signal = AbortSignal.any([this.#signal, timeout]);
|
|
52864
|
-
const response = await this.#fetcher(new URL(
|
|
52193
|
+
const response = await this.#fetcher(new URL(path12, this.#origin), {
|
|
52865
52194
|
method: "POST",
|
|
52866
52195
|
headers: {
|
|
52867
52196
|
authorization: workerToken ? `Worker ${workerToken}` : `Device ${this.#deviceToken}`,
|
|
@@ -53018,27 +52347,6 @@ function accessProfileSummary(accessProfile) {
|
|
|
53018
52347
|
return "System access: clients can modify files and run commands with this account's full environment, permissions, credentials, and network access.";
|
|
53019
52348
|
}
|
|
53020
52349
|
}
|
|
53021
|
-
function accessProfileNotice(status, requestedProfile) {
|
|
53022
|
-
if (status.state !== "connected" || !requestedProfile || status.accessProfileAccepted !== false) {
|
|
53023
|
-
return void 0;
|
|
53024
|
-
}
|
|
53025
|
-
return "The relay needs an update before this access profile can be shown to connected clients. Local profile enforcement remains active.";
|
|
53026
|
-
}
|
|
53027
|
-
function workspaceLabelNotice(status, requestedLabel) {
|
|
53028
|
-
if (status.state !== "connected" || !requestedLabel || status.workspaceLabelAccepted !== false) {
|
|
53029
|
-
return void 0;
|
|
53030
|
-
}
|
|
53031
|
-
return "The relay needs an update before workspace labels are available. This workspace is online without the requested label.";
|
|
53032
|
-
}
|
|
53033
|
-
var legacyRelayNotice = "The relay needs an update before this computer can expose several workspaces at once.";
|
|
53034
|
-
function combinedCompatibilityNotice(labelNotice, profileNotice, includeLegacyRelayNotice) {
|
|
53035
|
-
const messages = [
|
|
53036
|
-
labelNotice,
|
|
53037
|
-
profileNotice,
|
|
53038
|
-
includeLegacyRelayNotice ? legacyRelayNotice : void 0
|
|
53039
|
-
].filter((message) => Boolean(message));
|
|
53040
|
-
return messages.length > 0 ? messages.join(" ") : void 0;
|
|
53041
|
-
}
|
|
53042
52350
|
function statusMessage(status, connectedBefore) {
|
|
53043
52351
|
if (status.state === "connecting") return "Connecting to Glossa...";
|
|
53044
52352
|
if (status.state === "connected") {
|
|
@@ -53053,15 +52361,12 @@ function statusMessage(status, connectedBefore) {
|
|
|
53053
52361
|
async function connectRemoteWorker(endpoints, device, worker, options, signal, onConnected) {
|
|
53054
52362
|
let connectionState;
|
|
53055
52363
|
let connectedBefore = false;
|
|
53056
|
-
let labelNoticeShown = false;
|
|
53057
|
-
let profileNoticeShown = false;
|
|
53058
|
-
let legacyNoticeShown = false;
|
|
53059
52364
|
let connectHintTask;
|
|
53060
52365
|
const remoteWorker = new RemoteWorker({
|
|
53061
52366
|
origin: endpoints.workerOrigin,
|
|
53062
52367
|
deviceToken: device.token,
|
|
53063
52368
|
...options.workerVersion ? { workerVersion: options.workerVersion } : {},
|
|
53064
|
-
|
|
52369
|
+
accessProfile: options.accessProfile ?? DEFAULT_WORKER_ACCESS_PROFILE,
|
|
53065
52370
|
...options.workspaceLabel ? { workspaceLabel: options.workspaceLabel } : {},
|
|
53066
52371
|
worker: visibleWorker(worker, options),
|
|
53067
52372
|
signal,
|
|
@@ -53075,25 +52380,7 @@ async function connectRemoteWorker(endpoints, device, worker, options, signal, o
|
|
|
53075
52380
|
} else {
|
|
53076
52381
|
options.onEvent?.({ type: "status", status });
|
|
53077
52382
|
}
|
|
53078
|
-
|
|
53079
|
-
const profileNotice = profileNoticeShown ? void 0 : accessProfileNotice(status, options.accessProfile);
|
|
53080
|
-
const includeLegacyNotice = status.state === "connected" && status.legacyRelay && !legacyNoticeShown;
|
|
53081
|
-
const compatibilityNotice = combinedCompatibilityNotice(
|
|
53082
|
-
labelNotice,
|
|
53083
|
-
profileNotice,
|
|
53084
|
-
includeLegacyNotice
|
|
53085
|
-
);
|
|
53086
|
-
if (labelNotice) labelNoticeShown = true;
|
|
53087
|
-
if (profileNotice) profileNoticeShown = true;
|
|
53088
|
-
if (includeLegacyNotice) legacyNoticeShown = true;
|
|
53089
|
-
if (compatibilityNotice) {
|
|
53090
|
-
report(
|
|
53091
|
-
options,
|
|
53092
|
-
{ type: "notice", message: compatibilityNotice },
|
|
53093
|
-
compatibilityNotice
|
|
53094
|
-
);
|
|
53095
|
-
}
|
|
53096
|
-
if (status.state === "connected" && !status.reconnected && !compatibilityNotice && shouldShowConnectHint(endpoints.relayOrigin) && !connectHintTask) {
|
|
52383
|
+
if (status.state === "connected" && !status.reconnected && shouldShowConnectHint(endpoints.relayOrigin) && !connectHintTask) {
|
|
53097
52384
|
connectHintTask = announceConnectHint(
|
|
53098
52385
|
connectHintStore(),
|
|
53099
52386
|
(message) => {
|
|
@@ -53258,12 +52545,12 @@ function formatByteCount(value) {
|
|
|
53258
52545
|
}
|
|
53259
52546
|
return `${(kibibytes / 1024).toFixed(1)} MiB`;
|
|
53260
52547
|
}
|
|
53261
|
-
function workspacePath(
|
|
53262
|
-
return
|
|
52548
|
+
function workspacePath(path12) {
|
|
52549
|
+
return path12 || ".";
|
|
53263
52550
|
}
|
|
53264
|
-
function pathSummary(
|
|
52551
|
+
function pathSummary(path12, details = []) {
|
|
53265
52552
|
return {
|
|
53266
|
-
target: `path ${quoteActivityInput(
|
|
52553
|
+
target: `path ${quoteActivityInput(path12)}`,
|
|
53267
52554
|
details,
|
|
53268
52555
|
truncation: "middle"
|
|
53269
52556
|
};
|
|
@@ -53555,7 +52842,6 @@ function primaryFooterHints() {
|
|
|
53555
52842
|
{ key: "W", label: "Workspace" },
|
|
53556
52843
|
{ key: "D", label: "Devices" },
|
|
53557
52844
|
{ key: "?", label: "Help" },
|
|
53558
|
-
{ key: "L", label: "Account sign out", tone: COLORS.coral },
|
|
53559
52845
|
{ key: "Q", label: "Quit", tone: COLORS.coral }
|
|
53560
52846
|
];
|
|
53561
52847
|
}
|
|
@@ -53626,12 +52912,6 @@ function buildFooterRows(state, usable) {
|
|
|
53626
52912
|
function promptText(state) {
|
|
53627
52913
|
if (state.busy) return { message: "Working\u2026" };
|
|
53628
52914
|
if (!state.prompt) return void 0;
|
|
53629
|
-
if (state.prompt.type === "logout") {
|
|
53630
|
-
return {
|
|
53631
|
-
message: "Sign out the CLI account and disconnect? Computer pairing stays active.",
|
|
53632
|
-
choices: "Y confirm N cancel"
|
|
53633
|
-
};
|
|
53634
|
-
}
|
|
53635
52915
|
if (state.prompt.type === "access-confirm") {
|
|
53636
52916
|
const detail = state.prompt.accessProfile === "system" ? "Commands will inherit this OS account's permissions." : "This allows guarded file writes.";
|
|
53637
52917
|
return {
|
|
@@ -53918,7 +53198,7 @@ function DevicesView({ state, usable, bodyBudget, color }) {
|
|
|
53918
53198
|
const devices = state.status?.devices.slice(window2.start, window2.end) ?? [];
|
|
53919
53199
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { height: bodyBudget, flexDirection: "column", flexShrink: 0, overflow: "hidden", children: [
|
|
53920
53200
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Blank, {}),
|
|
53921
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { color, children: "
|
|
53201
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { color, children: "Paired" }),
|
|
53922
53202
|
state.statusLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
53923
53203
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Blank, {}),
|
|
53924
53204
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { color: color ? COLORS.muted : void 0, children: "Loading devices\u2026" })
|
|
@@ -53926,7 +53206,7 @@ function DevicesView({ state, usable, bodyBudget, color }) {
|
|
|
53926
53206
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Blank, {}),
|
|
53927
53207
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { color: color ? COLORS.muted : void 0, children: "Devices are not loaded." })
|
|
53928
53208
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
53929
|
-
/* @__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" }),
|
|
53930
53210
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text2, { color: color ? COLORS.muted : void 0, wrap: "truncate", children: state.status.relay }),
|
|
53931
53211
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Blank, {}),
|
|
53932
53212
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { color, children: "Overview" }),
|
|
@@ -53978,7 +53258,6 @@ function HelpView({ bodyBudget, color }) {
|
|
|
53978
53258
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { color, tone: COLORS.coral, children: "Manage" }),
|
|
53979
53259
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(HelpRow, { keyLabel: "\u2190/\u2192", label: "Change workspace access", color, tone: COLORS.coral }),
|
|
53980
53260
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(HelpRow, { keyLabel: "Enter/R", label: "Revoke selected device", color, tone: COLORS.coral }),
|
|
53981
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(HelpRow, { keyLabel: "L", label: "Account sign out", color, tone: COLORS.coral }),
|
|
53982
53261
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Blank, {}),
|
|
53983
53262
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionTitle, { color, children: "App" }),
|
|
53984
53263
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(HelpRow, { keyLabel: "Q", label: "Disconnect and quit", color, tone: COLORS.coral }),
|
|
@@ -54067,9 +53346,9 @@ function HudScreen({ state, columns, rows, color = true, now = Date.now() }) {
|
|
|
54067
53346
|
] });
|
|
54068
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 });
|
|
54069
53348
|
}
|
|
54070
|
-
async function loadDevices(
|
|
54071
|
-
const current =
|
|
54072
|
-
|
|
53349
|
+
async function loadDevices(store2, actions, signal) {
|
|
53350
|
+
const current = store2.getSnapshot();
|
|
53351
|
+
store2.update((state) => ({
|
|
54073
53352
|
...state,
|
|
54074
53353
|
view: "devices",
|
|
54075
53354
|
prompt: void 0,
|
|
@@ -54077,20 +53356,20 @@ async function loadDevices(store3, actions, signal) {
|
|
|
54077
53356
|
}));
|
|
54078
53357
|
if (current.statusLoading) return;
|
|
54079
53358
|
if (current.connection !== "connected" && current.connection !== "retrying") {
|
|
54080
|
-
|
|
53359
|
+
store2.update((state) => ({
|
|
54081
53360
|
...state,
|
|
54082
53361
|
notice: "Devices are available after Glossa connects."
|
|
54083
53362
|
}));
|
|
54084
53363
|
return;
|
|
54085
53364
|
}
|
|
54086
|
-
|
|
53365
|
+
store2.update((state) => ({
|
|
54087
53366
|
...state,
|
|
54088
53367
|
statusLoading: true
|
|
54089
53368
|
}));
|
|
54090
53369
|
try {
|
|
54091
53370
|
const status = await actions.loadStatus(signal);
|
|
54092
53371
|
if (signal.aborted) return;
|
|
54093
|
-
|
|
53372
|
+
store2.update((state) => ({
|
|
54094
53373
|
...state,
|
|
54095
53374
|
status,
|
|
54096
53375
|
deviceSelection: Math.min(
|
|
@@ -54101,24 +53380,24 @@ async function loadDevices(store3, actions, signal) {
|
|
|
54101
53380
|
}));
|
|
54102
53381
|
} catch (error46) {
|
|
54103
53382
|
if (signal.aborted) return;
|
|
54104
|
-
|
|
53383
|
+
store2.update((state) => ({
|
|
54105
53384
|
...state,
|
|
54106
53385
|
statusLoading: false,
|
|
54107
53386
|
notice: error46 instanceof Error ? error46.message : String(error46)
|
|
54108
53387
|
}));
|
|
54109
53388
|
}
|
|
54110
53389
|
}
|
|
54111
|
-
function beginAccessChange(
|
|
53390
|
+
function beginAccessChange(store2, actions, accessProfile) {
|
|
54112
53391
|
actions.changeAccessProfile(accessProfile);
|
|
54113
|
-
|
|
53392
|
+
store2.update((state) => ({
|
|
54114
53393
|
...state,
|
|
54115
53394
|
pendingAccessProfile: accessProfile,
|
|
54116
53395
|
prompt: void 0,
|
|
54117
53396
|
notice: void 0
|
|
54118
53397
|
}));
|
|
54119
53398
|
}
|
|
54120
|
-
function HudRuntime({ store:
|
|
54121
|
-
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);
|
|
54122
53401
|
const { columns, rows } = use_window_size_default();
|
|
54123
53402
|
const [, setClock] = (0, import_react34.useState)(0);
|
|
54124
53403
|
(0, import_react34.useEffect)(() => {
|
|
@@ -54127,7 +53406,7 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54127
53406
|
return () => clearInterval(timer);
|
|
54128
53407
|
}, [state.view, state.activities.length]);
|
|
54129
53408
|
(0, import_react34.useEffect)(() => {
|
|
54130
|
-
|
|
53409
|
+
store2.update((current) => {
|
|
54131
53410
|
let next = current;
|
|
54132
53411
|
const metrics = screenMetrics(current, columns, rows);
|
|
54133
53412
|
if (current.view === "activity") {
|
|
@@ -54149,9 +53428,9 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54149
53428
|
}
|
|
54150
53429
|
return next;
|
|
54151
53430
|
});
|
|
54152
|
-
}, [columns, rows,
|
|
53431
|
+
}, [columns, rows, store2]);
|
|
54153
53432
|
use_input_default((input, key) => {
|
|
54154
|
-
const current =
|
|
53433
|
+
const current = store2.getSnapshot();
|
|
54155
53434
|
const value = input.toLowerCase();
|
|
54156
53435
|
if (key.ctrl && value === "c" || value === "q") {
|
|
54157
53436
|
stop();
|
|
@@ -54160,21 +53439,17 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54160
53439
|
if (current.busy) return;
|
|
54161
53440
|
if (current.prompt) {
|
|
54162
53441
|
if (key.escape || value === "n") {
|
|
54163
|
-
|
|
53442
|
+
store2.update((state2) => ({ ...state2, prompt: void 0, notice: void 0 }));
|
|
54164
53443
|
return;
|
|
54165
53444
|
}
|
|
54166
53445
|
if (value !== "y") return;
|
|
54167
|
-
if (current.prompt.type === "logout") {
|
|
54168
|
-
stop("logout");
|
|
54169
|
-
return;
|
|
54170
|
-
}
|
|
54171
53446
|
if (current.prompt.type === "access-confirm") {
|
|
54172
|
-
beginAccessChange(
|
|
53447
|
+
beginAccessChange(store2, actions, current.prompt.accessProfile);
|
|
54173
53448
|
return;
|
|
54174
53449
|
}
|
|
54175
53450
|
const device = current.status?.devices[current.prompt.deviceIndex];
|
|
54176
53451
|
if (!device) return;
|
|
54177
|
-
|
|
53452
|
+
store2.update((state2) => ({
|
|
54178
53453
|
...state2,
|
|
54179
53454
|
busy: true,
|
|
54180
53455
|
prompt: void 0,
|
|
@@ -54182,13 +53457,13 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54182
53457
|
}));
|
|
54183
53458
|
void actions.revokeDevice(device.id, signal).then(async () => {
|
|
54184
53459
|
if (signal.aborted) return;
|
|
54185
|
-
|
|
54186
|
-
await loadDevices(
|
|
53460
|
+
store2.update((state2) => ({ ...state2, busy: false }));
|
|
53461
|
+
await loadDevices(store2, actions, signal);
|
|
54187
53462
|
if (signal.aborted) return;
|
|
54188
|
-
|
|
53463
|
+
store2.update((state2) => ({ ...state2, notice: `Revoked ${device.name}.` }));
|
|
54189
53464
|
}).catch((error46) => {
|
|
54190
53465
|
if (signal.aborted) return;
|
|
54191
|
-
|
|
53466
|
+
store2.update((state2) => ({
|
|
54192
53467
|
...state2,
|
|
54193
53468
|
busy: false,
|
|
54194
53469
|
notice: error46 instanceof Error ? error46.message : String(error46)
|
|
@@ -54202,7 +53477,7 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54202
53477
|
const maxPage = activityMaxPage(current.activities.length, capacity);
|
|
54203
53478
|
const page = key.upArrow ? Math.min(maxPage, current.activityPage + 1) : Math.max(0, current.activityPage - 1);
|
|
54204
53479
|
if (page !== current.activityPage) {
|
|
54205
|
-
|
|
53480
|
+
store2.update((state2) => ({ ...state2, activityPage: page }));
|
|
54206
53481
|
}
|
|
54207
53482
|
return;
|
|
54208
53483
|
}
|
|
@@ -54214,7 +53489,7 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54214
53489
|
}
|
|
54215
53490
|
const selection = key.upArrow ? Math.max(0, current.deviceSelection - 1) : Math.min(total - 1, current.deviceSelection + 1);
|
|
54216
53491
|
if (selection !== current.deviceSelection) {
|
|
54217
|
-
|
|
53492
|
+
store2.update((state2) => ({ ...state2, deviceSelection: selection, notice: void 0 }));
|
|
54218
53493
|
}
|
|
54219
53494
|
return;
|
|
54220
53495
|
}
|
|
@@ -54223,9 +53498,9 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54223
53498
|
const accessProfile = adjacentAccessProfile(current.accessProfile, direction);
|
|
54224
53499
|
if (!accessProfile) return;
|
|
54225
53500
|
if (direction < 0) {
|
|
54226
|
-
beginAccessChange(
|
|
53501
|
+
beginAccessChange(store2, actions, accessProfile);
|
|
54227
53502
|
} else {
|
|
54228
|
-
|
|
53503
|
+
store2.update((state2) => ({
|
|
54229
53504
|
...state2,
|
|
54230
53505
|
prompt: { type: "access-confirm", accessProfile },
|
|
54231
53506
|
notice: void 0
|
|
@@ -54234,11 +53509,11 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54234
53509
|
return;
|
|
54235
53510
|
}
|
|
54236
53511
|
if (key.escape) {
|
|
54237
|
-
|
|
53512
|
+
store2.update((state2) => ({ ...state2, view: "workspace", notice: void 0 }));
|
|
54238
53513
|
return;
|
|
54239
53514
|
}
|
|
54240
53515
|
if (value === "a") {
|
|
54241
|
-
|
|
53516
|
+
store2.update((state2) => ({
|
|
54242
53517
|
...state2,
|
|
54243
53518
|
view: "activity",
|
|
54244
53519
|
activityPage: current.view === "activity" ? state2.activityPage : 0,
|
|
@@ -54247,7 +53522,7 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54247
53522
|
return;
|
|
54248
53523
|
}
|
|
54249
53524
|
if (value === "w") {
|
|
54250
|
-
|
|
53525
|
+
store2.update((state2) => ({
|
|
54251
53526
|
...state2,
|
|
54252
53527
|
view: "workspace",
|
|
54253
53528
|
prompt: void 0,
|
|
@@ -54257,27 +53532,27 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54257
53532
|
}
|
|
54258
53533
|
if (value === "d") {
|
|
54259
53534
|
if (current.status) {
|
|
54260
|
-
|
|
53535
|
+
store2.update((state2) => ({
|
|
54261
53536
|
...state2,
|
|
54262
53537
|
view: "devices",
|
|
54263
53538
|
prompt: void 0,
|
|
54264
53539
|
notice: void 0
|
|
54265
53540
|
}));
|
|
54266
53541
|
} else {
|
|
54267
|
-
void loadDevices(
|
|
53542
|
+
void loadDevices(store2, actions, signal);
|
|
54268
53543
|
}
|
|
54269
53544
|
return;
|
|
54270
53545
|
}
|
|
54271
53546
|
if ((key.return || input === "\r" || input === "\n" || value === "r") && current.view === "devices") {
|
|
54272
53547
|
const count = current.status?.devices.length ?? 0;
|
|
54273
53548
|
if (count === 0) {
|
|
54274
|
-
|
|
53549
|
+
store2.update((state2) => ({ ...state2, notice: "There are no devices to revoke." }));
|
|
54275
53550
|
return;
|
|
54276
53551
|
}
|
|
54277
53552
|
const metrics = screenMetrics(current, columns, rows);
|
|
54278
53553
|
const capacity = statusDeviceCapacity(current, metrics.bodyBudget, metrics.usable);
|
|
54279
53554
|
const selection = Math.min(current.deviceSelection, count - 1);
|
|
54280
|
-
|
|
53555
|
+
store2.update((state2) => capacity === 0 ? { ...state2, notice: "Increase the terminal height to choose a device." } : {
|
|
54281
53556
|
...state2,
|
|
54282
53557
|
deviceSelection: selection,
|
|
54283
53558
|
prompt: { type: "revoke-confirm", deviceIndex: selection },
|
|
@@ -54285,16 +53560,8 @@ function HudRuntime({ store: store3, actions, signal, stop }) {
|
|
|
54285
53560
|
});
|
|
54286
53561
|
return;
|
|
54287
53562
|
}
|
|
54288
|
-
if (value === "l") {
|
|
54289
|
-
store3.update((state2) => ({
|
|
54290
|
-
...state2,
|
|
54291
|
-
prompt: { type: "logout" },
|
|
54292
|
-
notice: void 0
|
|
54293
|
-
}));
|
|
54294
|
-
return;
|
|
54295
|
-
}
|
|
54296
53563
|
if (input === "?") {
|
|
54297
|
-
|
|
53564
|
+
store2.update((state2) => ({
|
|
54298
53565
|
...state2,
|
|
54299
53566
|
view: "help",
|
|
54300
53567
|
notice: void 0
|
|
@@ -54317,11 +53584,9 @@ async function runSessionHud(actions, input = process.stdin, output = process.st
|
|
|
54317
53584
|
throw new Error("Glossa requires an interactive terminal.");
|
|
54318
53585
|
}
|
|
54319
53586
|
const controller = new AbortController();
|
|
54320
|
-
const
|
|
54321
|
-
let exitAction = "quit";
|
|
53587
|
+
const store2 = new HudStore(actions.workspace);
|
|
54322
53588
|
let instance;
|
|
54323
|
-
const stop = (
|
|
54324
|
-
exitAction = action;
|
|
53589
|
+
const stop = () => {
|
|
54325
53590
|
controller.abort();
|
|
54326
53591
|
instance?.unmount();
|
|
54327
53592
|
};
|
|
@@ -54333,7 +53598,7 @@ async function runSessionHud(actions, input = process.stdin, output = process.st
|
|
|
54333
53598
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
54334
53599
|
HudRuntime,
|
|
54335
53600
|
{
|
|
54336
|
-
store:
|
|
53601
|
+
store: store2,
|
|
54337
53602
|
actions,
|
|
54338
53603
|
signal: controller.signal,
|
|
54339
53604
|
stop
|
|
@@ -54350,13 +53615,13 @@ async function runSessionHud(actions, input = process.stdin, output = process.st
|
|
|
54350
53615
|
maxFps: 30
|
|
54351
53616
|
}
|
|
54352
53617
|
);
|
|
54353
|
-
const session = actions.run(controller.signal, (event) =>
|
|
53618
|
+
const session = actions.run(controller.signal, (event) => store2.event(event)).then(() => {
|
|
54354
53619
|
if (!controller.signal.aborted) {
|
|
54355
|
-
|
|
53620
|
+
store2.update((state) => ({ ...state, connection: "disconnected" }));
|
|
54356
53621
|
}
|
|
54357
53622
|
}).catch((error46) => {
|
|
54358
53623
|
if (!controller.signal.aborted) {
|
|
54359
|
-
|
|
53624
|
+
store2.update((state) => ({
|
|
54360
53625
|
...state,
|
|
54361
53626
|
connection: "error",
|
|
54362
53627
|
message: error46 instanceof Error ? error46.message : String(error46)
|
|
@@ -54369,7 +53634,6 @@ async function runSessionHud(actions, input = process.stdin, output = process.st
|
|
|
54369
53634
|
try {
|
|
54370
53635
|
await instance.waitUntilExit();
|
|
54371
53636
|
await session;
|
|
54372
|
-
return exitAction;
|
|
54373
53637
|
} finally {
|
|
54374
53638
|
controller.abort();
|
|
54375
53639
|
process.removeListener("SIGINT", stopFromSignal);
|
|
@@ -54379,10 +53643,10 @@ async function runSessionHud(actions, input = process.stdin, output = process.st
|
|
|
54379
53643
|
|
|
54380
53644
|
// src/update-service.ts
|
|
54381
53645
|
var import_semver = __toESM(require_semver2(), 1);
|
|
54382
|
-
import { spawn as
|
|
53646
|
+
import { spawn as spawn2 } from "node:child_process";
|
|
54383
53647
|
import { createHash as createHash2 } from "node:crypto";
|
|
54384
53648
|
import { chmod as chmod3, readdir as readdir2, rename as rename2, rm as rm5, writeFile as writeFile6 } from "node:fs/promises";
|
|
54385
|
-
import
|
|
53649
|
+
import path8 from "node:path";
|
|
54386
53650
|
var PACKAGE_NAME = "@ariobarin/glossa";
|
|
54387
53651
|
var DEFAULT_REGISTRY_URL = "https://registry.npmjs.org/@ariobarin%2Fglossa";
|
|
54388
53652
|
var DEFAULT_RELEASE_BASE_URL = "https://github.com/ariobarin/glossa/releases/download";
|
|
@@ -54459,8 +53723,8 @@ async function cleanupUpdateBackups(distribution, options = {}) {
|
|
|
54459
53723
|
const platform2 = options.platform ?? process.platform;
|
|
54460
53724
|
if (distribution !== "standalone" || platform2 !== "win32") return;
|
|
54461
53725
|
const executable = options.executablePath ?? process.execPath;
|
|
54462
|
-
const directory =
|
|
54463
|
-
const prefix = `${
|
|
53726
|
+
const directory = path8.dirname(executable);
|
|
53727
|
+
const prefix = `${path8.basename(executable)}.old-`;
|
|
54464
53728
|
let entries;
|
|
54465
53729
|
try {
|
|
54466
53730
|
entries = await readdir2(directory, { withFileTypes: true });
|
|
@@ -54473,7 +53737,7 @@ async function cleanupUpdateBackups(distribution, options = {}) {
|
|
|
54473
53737
|
const ownerPid = backupOwnerPid(entry.name, prefix);
|
|
54474
53738
|
return ownerPid !== null && !processIsAlive(ownerPid);
|
|
54475
53739
|
}).map(async (entry) => {
|
|
54476
|
-
await rm5(
|
|
53740
|
+
await rm5(path8.join(directory, entry.name), { force: true }).catch(() => void 0);
|
|
54477
53741
|
})
|
|
54478
53742
|
);
|
|
54479
53743
|
}
|
|
@@ -54514,7 +53778,7 @@ function npmInstallInvocation(version2, platform2 = process.platform, commandShe
|
|
|
54514
53778
|
async function runNpmInstall(version2) {
|
|
54515
53779
|
const { command, args } = npmInstallInvocation(version2);
|
|
54516
53780
|
await new Promise((resolve, reject) => {
|
|
54517
|
-
const child =
|
|
53781
|
+
const child = spawn2(command, args, { stdio: "inherit" });
|
|
54518
53782
|
child.once("error", reject);
|
|
54519
53783
|
child.once("close", (status) => {
|
|
54520
53784
|
if (status === 0) resolve();
|
|
@@ -54549,9 +53813,9 @@ async function installStandaloneUpdate(info, options) {
|
|
|
54549
53813
|
if (actual !== expected) {
|
|
54550
53814
|
throw new Error("Glossa refused to update because the SHA-256 checksum did not match.");
|
|
54551
53815
|
}
|
|
54552
|
-
const download =
|
|
54553
|
-
|
|
54554
|
-
`${
|
|
53816
|
+
const download = path8.join(
|
|
53817
|
+
path8.dirname(destination),
|
|
53818
|
+
`${path8.basename(destination)}.download-${process.pid}`
|
|
54555
53819
|
);
|
|
54556
53820
|
try {
|
|
54557
53821
|
await writeFile6(download, binary, { mode: 493 });
|
|
@@ -54578,13 +53842,13 @@ async function installUpdate(info, distribution, options = {}) {
|
|
|
54578
53842
|
|
|
54579
53843
|
// src/update-state.ts
|
|
54580
53844
|
import { chmod as chmod4, mkdir as mkdir6, readFile as readFile5, rename as rename3, rm as rm6, writeFile as writeFile7 } from "node:fs/promises";
|
|
54581
|
-
import
|
|
53845
|
+
import path9 from "node:path";
|
|
54582
53846
|
var UPDATE_CHECK_INTERVAL_MS = 24 * 60 * 60 * 1e3;
|
|
54583
53847
|
function defaultUpdateChannel(version2) {
|
|
54584
53848
|
return version2.includes("-") ? "beta" : "stable";
|
|
54585
53849
|
}
|
|
54586
53850
|
function updateStateFile() {
|
|
54587
|
-
return
|
|
53851
|
+
return path9.join(configDirectory(), "updates.json");
|
|
54588
53852
|
}
|
|
54589
53853
|
function isPolicy(value) {
|
|
54590
53854
|
return value === "notify" || value === "auto" || value === "off";
|
|
@@ -54603,12 +53867,10 @@ async function loadUpdateState(currentVersion, file2 = updateStateFile()) {
|
|
|
54603
53867
|
try {
|
|
54604
53868
|
const parsed = JSON.parse(await readFile5(file2, "utf8"));
|
|
54605
53869
|
const lastCheckedAt = optionalString(parsed.lastCheckedAt);
|
|
54606
|
-
const latestVersion = optionalString(parsed.latestVersion);
|
|
54607
53870
|
return {
|
|
54608
53871
|
policy: isPolicy(parsed.policy) ? parsed.policy : defaults.policy,
|
|
54609
53872
|
channel: isChannel(parsed.channel) ? parsed.channel : defaults.channel,
|
|
54610
|
-
...lastCheckedAt ? { lastCheckedAt } : {}
|
|
54611
|
-
...latestVersion ? { latestVersion } : {}
|
|
53873
|
+
...lastCheckedAt ? { lastCheckedAt } : {}
|
|
54612
53874
|
};
|
|
54613
53875
|
} catch (error46) {
|
|
54614
53876
|
if (error46.code === "ENOENT" || error46 instanceof SyntaxError) {
|
|
@@ -54618,7 +53880,7 @@ async function loadUpdateState(currentVersion, file2 = updateStateFile()) {
|
|
|
54618
53880
|
}
|
|
54619
53881
|
}
|
|
54620
53882
|
async function saveUpdateState(state, file2 = updateStateFile()) {
|
|
54621
|
-
const directory =
|
|
53883
|
+
const directory = path9.dirname(file2);
|
|
54622
53884
|
const temporary = `${file2}.${process.pid}.tmp`;
|
|
54623
53885
|
await mkdir6(directory, { recursive: true, mode: 448 });
|
|
54624
53886
|
try {
|
|
@@ -54642,11 +53904,10 @@ async function configureUpdates(currentVersion, changes, file2 = updateStateFile
|
|
|
54642
53904
|
await saveUpdateState(state, file2);
|
|
54643
53905
|
return state;
|
|
54644
53906
|
}
|
|
54645
|
-
async function recordUpdateCheck(currentVersion,
|
|
53907
|
+
async function recordUpdateCheck(currentVersion, checkedAt = /* @__PURE__ */ new Date(), file2 = updateStateFile()) {
|
|
54646
53908
|
const state = {
|
|
54647
53909
|
...await loadUpdateState(currentVersion, file2),
|
|
54648
|
-
lastCheckedAt: checkedAt.toISOString()
|
|
54649
|
-
latestVersion
|
|
53910
|
+
lastCheckedAt: checkedAt.toISOString()
|
|
54650
53911
|
};
|
|
54651
53912
|
await saveUpdateState(state, file2);
|
|
54652
53913
|
return state;
|
|
@@ -54673,41 +53934,18 @@ async function unpairComputer(dependencies = {}) {
|
|
|
54673
53934
|
log("Unpaired this computer from Glossa.");
|
|
54674
53935
|
}
|
|
54675
53936
|
|
|
54676
|
-
// src/usage-store.ts
|
|
54677
|
-
import { appendFile, chmod as chmod5, mkdir as mkdir7, readFile as readFile6 } from "node:fs/promises";
|
|
54678
|
-
import path11 from "node:path";
|
|
54679
|
-
function usageFile() {
|
|
54680
|
-
return path11.join(configDirectory(), "usage.jsonl");
|
|
54681
|
-
}
|
|
54682
|
-
async function recordUsageEvent(event, file2 = usageFile()) {
|
|
54683
|
-
const directory = path11.dirname(file2);
|
|
54684
|
-
await mkdir7(directory, { recursive: true, mode: 448 });
|
|
54685
|
-
await appendFile(file2, `${JSON.stringify(event)}
|
|
54686
|
-
`, {
|
|
54687
|
-
encoding: "utf8",
|
|
54688
|
-
mode: 384
|
|
54689
|
-
});
|
|
54690
|
-
if (process.platform !== "win32") await chmod5(file2, 384);
|
|
54691
|
-
}
|
|
54692
|
-
async function recordSessionUsage(at = /* @__PURE__ */ new Date(), file2 = usageFile()) {
|
|
54693
|
-
await recordUsageEvent({ v: 1, at: at.toISOString(), kind: "session" }, file2);
|
|
54694
|
-
}
|
|
54695
|
-
async function recordToolUsage(tool, ok, at = /* @__PURE__ */ new Date(), file2 = usageFile()) {
|
|
54696
|
-
await recordUsageEvent({ v: 1, at: at.toISOString(), kind: "tool", tool, ok }, file2);
|
|
54697
|
-
}
|
|
54698
|
-
|
|
54699
53937
|
// src/worker/root-selection.ts
|
|
54700
53938
|
import { realpath as realpath2 } from "node:fs/promises";
|
|
54701
53939
|
import os6 from "node:os";
|
|
54702
|
-
import
|
|
53940
|
+
import path10 from "node:path";
|
|
54703
53941
|
function containsPath(root, candidate) {
|
|
54704
|
-
const relative =
|
|
54705
|
-
return relative !== "" && relative !== ".." && !relative.startsWith(`..${
|
|
53942
|
+
const relative = path10.relative(root, candidate);
|
|
53943
|
+
return relative !== "" && relative !== ".." && !relative.startsWith(`..${path10.sep}`) && !path10.isAbsolute(relative);
|
|
54706
53944
|
}
|
|
54707
53945
|
async function rejectImplicitHomeAncestor(root) {
|
|
54708
53946
|
const homes = await Promise.all(
|
|
54709
53947
|
[os6.homedir(), accountHomeDirectory()].map(
|
|
54710
|
-
async (home) => await realpath2(home).catch(() =>
|
|
53948
|
+
async (home) => await realpath2(home).catch(() => path10.resolve(home))
|
|
54711
53949
|
)
|
|
54712
53950
|
);
|
|
54713
53951
|
if (homes.some((home) => containsPath(root, home))) {
|
|
@@ -54725,9 +53963,9 @@ async function selectExposureRoot(explicitPath, cwd2 = process.cwd()) {
|
|
|
54725
53963
|
|
|
54726
53964
|
// src/worker/workspace-lease.ts
|
|
54727
53965
|
import { createHash as createHash3 } from "node:crypto";
|
|
54728
|
-
import { lstat as lstat3, mkdir as
|
|
53966
|
+
import { lstat as lstat3, mkdir as mkdir7, rm as rm7, stat as stat4 } from "node:fs/promises";
|
|
54729
53967
|
import net from "node:net";
|
|
54730
|
-
import
|
|
53968
|
+
import path11 from "node:path";
|
|
54731
53969
|
import { setTimeout as delay4 } from "node:timers/promises";
|
|
54732
53970
|
var GUARD_STALE_MS = 5e3;
|
|
54733
53971
|
var GUARD_RETRY_MS = 10;
|
|
@@ -54751,12 +53989,12 @@ function workspaceIdentity(root) {
|
|
|
54751
53989
|
}
|
|
54752
53990
|
function defaultLeaseDirectory() {
|
|
54753
53991
|
if (typeof process.getuid === "function") {
|
|
54754
|
-
return
|
|
53992
|
+
return path11.join("/tmp", `glossa-workspaces-${process.getuid()}`);
|
|
54755
53993
|
}
|
|
54756
|
-
return
|
|
53994
|
+
return path11.join(accountHomeDirectory(), ".glossa-workspace-leases");
|
|
54757
53995
|
}
|
|
54758
53996
|
async function ensureLeaseDirectory(directory) {
|
|
54759
|
-
await
|
|
53997
|
+
await mkdir7(directory, { recursive: true, mode: 448 });
|
|
54760
53998
|
const directoryStat = await lstat3(directory);
|
|
54761
53999
|
if (directoryStat.isSymbolicLink() || !directoryStat.isDirectory()) {
|
|
54762
54000
|
throw new Error("The Glossa workspace lease location is not a private directory.");
|
|
@@ -54773,14 +54011,14 @@ function endpointFor(identity, directory) {
|
|
|
54773
54011
|
if (process.platform === "win32") {
|
|
54774
54012
|
return `\\\\.\\pipe\\glossa-workspace-${identity}`;
|
|
54775
54013
|
}
|
|
54776
|
-
return
|
|
54014
|
+
return path11.join(directory, `${identity}.sock`);
|
|
54777
54015
|
}
|
|
54778
54016
|
async function acquireGuard2(directory, identity) {
|
|
54779
|
-
const guard =
|
|
54017
|
+
const guard = path11.join(directory, `${identity}.guard`);
|
|
54780
54018
|
const deadline = Date.now() + GUARD_TIMEOUT_MS;
|
|
54781
54019
|
while (true) {
|
|
54782
54020
|
try {
|
|
54783
|
-
await
|
|
54021
|
+
await mkdir7(guard, { mode: 448 });
|
|
54784
54022
|
return async () => await rm7(guard, { recursive: true, force: true });
|
|
54785
54023
|
} catch (error46) {
|
|
54786
54024
|
if (error46.code !== "EEXIST") throw error46;
|
|
@@ -54807,7 +54045,7 @@ async function acquireGuard2(directory, identity) {
|
|
|
54807
54045
|
function createLeaseServer() {
|
|
54808
54046
|
return net.createServer((socket) => socket.destroy());
|
|
54809
54047
|
}
|
|
54810
|
-
async function listen(server,
|
|
54048
|
+
async function listen(server, endpoint) {
|
|
54811
54049
|
await new Promise((resolve, reject) => {
|
|
54812
54050
|
const onError = (error46) => {
|
|
54813
54051
|
server.removeListener("listening", onListening);
|
|
@@ -54819,12 +54057,12 @@ async function listen(server, endpoint3) {
|
|
|
54819
54057
|
};
|
|
54820
54058
|
server.once("error", onError);
|
|
54821
54059
|
server.once("listening", onListening);
|
|
54822
|
-
server.listen(
|
|
54060
|
+
server.listen(endpoint);
|
|
54823
54061
|
});
|
|
54824
54062
|
}
|
|
54825
|
-
async function endpointIsActive(
|
|
54063
|
+
async function endpointIsActive(endpoint) {
|
|
54826
54064
|
return await new Promise((resolve) => {
|
|
54827
|
-
const socket = net.createConnection(
|
|
54065
|
+
const socket = net.createConnection(endpoint);
|
|
54828
54066
|
let settled = false;
|
|
54829
54067
|
const finish = (active) => {
|
|
54830
54068
|
if (settled) return;
|
|
@@ -54847,14 +54085,14 @@ async function acquireWorkspaceLease(root, options = {}) {
|
|
|
54847
54085
|
const userDirectory = options.directory ?? defaultLeaseDirectory();
|
|
54848
54086
|
await ensureLeaseDirectory(userDirectory);
|
|
54849
54087
|
const identity = workspaceIdentity(root);
|
|
54850
|
-
const
|
|
54088
|
+
const endpoint = endpointFor(identity, userDirectory);
|
|
54851
54089
|
const releaseGuard = await acquireGuard2(userDirectory, identity);
|
|
54852
54090
|
let server;
|
|
54853
54091
|
try {
|
|
54854
54092
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
54855
54093
|
server = createLeaseServer();
|
|
54856
54094
|
try {
|
|
54857
|
-
await listen(server,
|
|
54095
|
+
await listen(server, endpoint);
|
|
54858
54096
|
server.unref();
|
|
54859
54097
|
let released = false;
|
|
54860
54098
|
return {
|
|
@@ -54866,7 +54104,7 @@ async function acquireWorkspaceLease(root, options = {}) {
|
|
|
54866
54104
|
try {
|
|
54867
54105
|
await closeServer(server);
|
|
54868
54106
|
if (process.platform !== "win32") {
|
|
54869
|
-
await rm7(
|
|
54107
|
+
await rm7(endpoint, { force: true });
|
|
54870
54108
|
}
|
|
54871
54109
|
} finally {
|
|
54872
54110
|
await releaseGuard2();
|
|
@@ -54875,11 +54113,11 @@ async function acquireWorkspaceLease(root, options = {}) {
|
|
|
54875
54113
|
};
|
|
54876
54114
|
} catch (error46) {
|
|
54877
54115
|
await closeServer(server);
|
|
54878
|
-
if (await endpointIsActive(
|
|
54116
|
+
if (await endpointIsActive(endpoint)) {
|
|
54879
54117
|
throw new WorkspaceAlreadyActiveError();
|
|
54880
54118
|
}
|
|
54881
54119
|
if (error46.code !== "EADDRINUSE") throw error46;
|
|
54882
|
-
if (process.platform !== "win32") await rm7(
|
|
54120
|
+
if (process.platform !== "win32") await rm7(endpoint, { force: true });
|
|
54883
54121
|
}
|
|
54884
54122
|
}
|
|
54885
54123
|
throw new Error("Could not reserve this workspace for the current Glossa process.");
|
|
@@ -54889,15 +54127,12 @@ async function acquireWorkspaceLease(root, options = {}) {
|
|
|
54889
54127
|
}
|
|
54890
54128
|
|
|
54891
54129
|
// src/main.ts
|
|
54892
|
-
var VERSION = "0.2.
|
|
54130
|
+
var VERSION = "0.2.2";
|
|
54893
54131
|
var DISTRIBUTION = "npm";
|
|
54894
54132
|
var HELP = `Glossa ${VERSION}
|
|
54895
54133
|
|
|
54896
54134
|
Usage:
|
|
54897
54135
|
glossa [--access <read-only|workspace|system>] [--label <name>] [directory]
|
|
54898
|
-
glossa status
|
|
54899
|
-
glossa devices revoke <id>
|
|
54900
|
-
glossa logout
|
|
54901
54136
|
glossa unpair
|
|
54902
54137
|
glossa update [--check]
|
|
54903
54138
|
glossa update --policy <notify|auto|off>
|
|
@@ -54919,68 +54154,18 @@ Keys:
|
|
|
54919
54154
|
\u2190\u2192 change workspace access
|
|
54920
54155
|
Enter/r revoke selected device
|
|
54921
54156
|
Esc workspace
|
|
54922
|
-
l account sign out
|
|
54923
54157
|
? help
|
|
54924
54158
|
q disconnect and quit`;
|
|
54925
|
-
async function
|
|
54926
|
-
const
|
|
54927
|
-
const cancel = () => controller.abort();
|
|
54928
|
-
process.once("SIGINT", cancel);
|
|
54929
|
-
try {
|
|
54930
|
-
return await action(controller.signal);
|
|
54931
|
-
} finally {
|
|
54932
|
-
process.removeListener("SIGINT", cancel);
|
|
54933
|
-
}
|
|
54934
|
-
}
|
|
54935
|
-
async function authenticatedSession(signal) {
|
|
54936
|
-
if (signal) return await signedInSession({ ...loadAuthConfig(), signal });
|
|
54937
|
-
return await withLoginSignal(
|
|
54938
|
-
async (loginSignal) => await signedInSession({ ...loadAuthConfig(), signal: loginSignal })
|
|
54939
|
-
);
|
|
54940
|
-
}
|
|
54941
|
-
async function authenticatedCredentials(signal) {
|
|
54942
|
-
return (await authenticatedSession(signal)).credentials;
|
|
54943
|
-
}
|
|
54944
|
-
async function showStatus() {
|
|
54945
|
-
const credentials = await authenticatedCredentials();
|
|
54946
|
-
const endpoints = loadRelayEndpoints();
|
|
54947
|
-
const status = await new WorkspaceStatusService(
|
|
54948
|
-
credentials,
|
|
54949
|
-
endpoints
|
|
54950
|
-
).refresh();
|
|
54951
|
-
for (const line of formatStatus(status)) console.log(line);
|
|
54952
|
-
}
|
|
54953
|
-
function hudStatus(status) {
|
|
54954
|
-
return {
|
|
54955
|
-
...status,
|
|
54956
|
-
devices: status.devices.map((device) => ({
|
|
54957
|
-
id: device.id,
|
|
54958
|
-
name: device.name,
|
|
54959
|
-
platform: device.platform ?? "Unknown platform",
|
|
54960
|
-
lastSeen: formatRelativeTime(device.lastSeenAt),
|
|
54961
|
-
status: deviceStatus(device)
|
|
54962
|
-
}))
|
|
54963
|
-
};
|
|
54964
|
-
}
|
|
54965
|
-
async function revokeKnownDevice(deviceId) {
|
|
54966
|
-
const credentials = await authenticatedCredentials();
|
|
54967
|
-
await revokeDevice(loadRelayEndpoints(), credentials, deviceId);
|
|
54968
|
-
}
|
|
54969
|
-
async function runWorkspaceSession(path14, label, accessProfile) {
|
|
54970
|
-
const root = await selectExposureRoot(path14);
|
|
54159
|
+
async function runWorkspaceSession(path12, label, accessProfile) {
|
|
54160
|
+
const root = await selectExposureRoot(path12);
|
|
54971
54161
|
const lease = await acquireWorkspaceLease(root);
|
|
54972
|
-
let usageWrites = Promise.resolve();
|
|
54973
|
-
const queueUsage = (write) => {
|
|
54974
|
-
usageWrites = usageWrites.then(write).catch(() => void 0);
|
|
54975
|
-
};
|
|
54976
54162
|
try {
|
|
54977
54163
|
const endpoints = loadRelayEndpoints();
|
|
54978
54164
|
const device = await deviceForSession(endpoints);
|
|
54979
54165
|
let postExitNotice;
|
|
54980
54166
|
let requestedAccessProfile = accessProfile;
|
|
54981
54167
|
let activeSessionController;
|
|
54982
|
-
|
|
54983
|
-
const exitAction = await runSessionHud({
|
|
54168
|
+
await runSessionHud({
|
|
54984
54169
|
workspace: root,
|
|
54985
54170
|
...label ? { workspaceLabel: label } : {},
|
|
54986
54171
|
run: async (signal, onEvent) => {
|
|
@@ -55000,9 +54185,6 @@ async function runWorkspaceSession(path14, label, accessProfile) {
|
|
|
55000
54185
|
signal: sessionController.signal,
|
|
55001
54186
|
onEvent: (event) => {
|
|
55002
54187
|
postExitNotice = retainPostExitNotice(postExitNotice, event);
|
|
55003
|
-
if (event.type === "activity" && event.phase === "returned") {
|
|
55004
|
-
queueUsage(async () => await recordToolUsage(event.job.type, event.ok));
|
|
55005
|
-
}
|
|
55006
54188
|
onEvent(event);
|
|
55007
54189
|
},
|
|
55008
54190
|
quiet: true,
|
|
@@ -55024,16 +54206,24 @@ async function runWorkspaceSession(path14, label, accessProfile) {
|
|
|
55024
54206
|
}
|
|
55025
54207
|
},
|
|
55026
54208
|
loadStatus: async (signal) => {
|
|
55027
|
-
const
|
|
55028
|
-
|
|
55029
|
-
|
|
55030
|
-
|
|
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
|
+
};
|
|
55031
54222
|
},
|
|
55032
54223
|
revokeDevice: async (deviceId, signal) => {
|
|
55033
|
-
const credentials = await authenticatedCredentials(signal);
|
|
55034
54224
|
await revokeDevice(
|
|
55035
54225
|
endpoints,
|
|
55036
|
-
|
|
54226
|
+
`Device ${device.token}`,
|
|
55037
54227
|
deviceId,
|
|
55038
54228
|
async (input, init) => await fetch(input, { ...init, signal })
|
|
55039
54229
|
);
|
|
@@ -55045,20 +54235,18 @@ async function runWorkspaceSession(path14, label, accessProfile) {
|
|
|
55045
54235
|
}
|
|
55046
54236
|
});
|
|
55047
54237
|
if (postExitNotice) console.error(postExitNotice);
|
|
55048
|
-
if (exitAction === "logout") await logoutFromGlossa();
|
|
55049
54238
|
} finally {
|
|
55050
|
-
await usageWrites;
|
|
55051
54239
|
await lease.release();
|
|
55052
54240
|
}
|
|
55053
54241
|
}
|
|
55054
|
-
async function runWorkspace(
|
|
54242
|
+
async function runWorkspace(path12, label, accessProfile) {
|
|
55055
54243
|
await withWorkspaceLease(
|
|
55056
|
-
async () => await runWorkspaceSession(
|
|
54244
|
+
async () => await runWorkspaceSession(path12, label, accessProfile)
|
|
55057
54245
|
);
|
|
55058
54246
|
}
|
|
55059
54247
|
async function refreshUpdateInfo(timeoutMs) {
|
|
55060
54248
|
const info = await loadUpdateInfo(timeoutMs);
|
|
55061
|
-
await recordUpdateCheck(VERSION
|
|
54249
|
+
await recordUpdateCheck(VERSION);
|
|
55062
54250
|
return info;
|
|
55063
54251
|
}
|
|
55064
54252
|
async function loadUpdateInfo(timeoutMs) {
|
|
@@ -55113,11 +54301,11 @@ async function updateBeforeWorkspace() {
|
|
|
55113
54301
|
return false;
|
|
55114
54302
|
}
|
|
55115
54303
|
if (!info.updateAvailable) {
|
|
55116
|
-
await recordUpdateCheck(VERSION
|
|
54304
|
+
await recordUpdateCheck(VERSION);
|
|
55117
54305
|
return false;
|
|
55118
54306
|
}
|
|
55119
54307
|
if (state.policy === "notify") {
|
|
55120
|
-
await recordUpdateCheck(VERSION
|
|
54308
|
+
await recordUpdateCheck(VERSION);
|
|
55121
54309
|
console.error(
|
|
55122
54310
|
`Glossa ${info.availableVersion} is available. Run glossa update after disconnecting.`
|
|
55123
54311
|
);
|
|
@@ -55152,17 +54340,10 @@ async function main() {
|
|
|
55152
54340
|
invocation.label,
|
|
55153
54341
|
invocation.accessProfile
|
|
55154
54342
|
);
|
|
55155
|
-
} else if (invocation.command === "status") {
|
|
55156
|
-
await showStatus();
|
|
55157
|
-
} else if (invocation.command === "logout") {
|
|
55158
|
-
await logoutFromGlossa();
|
|
55159
54343
|
} else if (invocation.command === "unpair") {
|
|
55160
54344
|
await unpairComputer();
|
|
55161
54345
|
} else if (invocation.command === "update") {
|
|
55162
54346
|
await runUpdateCommand(invocation);
|
|
55163
|
-
} else {
|
|
55164
|
-
await revokeKnownDevice(invocation.deviceId);
|
|
55165
|
-
console.log(`Revoked device ${invocation.deviceId}. Running workspaces on it are disconnected.`);
|
|
55166
54347
|
}
|
|
55167
54348
|
}
|
|
55168
54349
|
main().catch((error46) => {
|