@mx-space/cli 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +33 -0
- package/README.md +375 -0
- package/ROADMAP.md +42 -0
- package/bin/mxs.cjs +40 -0
- package/dist/Lexical.dev-DpU35QTu.mjs +10992 -0
- package/dist/Lexical.dev-DpU35QTu.mjs.map +1 -0
- package/dist/Lexical.node-gYNDt_p9.mjs +247 -0
- package/dist/Lexical.node-gYNDt_p9.mjs.map +1 -0
- package/dist/Lexical.prod-t4c3bHV3.mjs +5579 -0
- package/dist/Lexical.prod-t4c3bHV3.mjs.map +1 -0
- package/dist/LexicalClipboard.dev-CdeeWCIq.mjs +496 -0
- package/dist/LexicalClipboard.dev-CdeeWCIq.mjs.map +1 -0
- package/dist/LexicalClipboard.node-0e9GBL1g.mjs +26 -0
- package/dist/LexicalClipboard.node-0e9GBL1g.mjs.map +1 -0
- package/dist/LexicalClipboard.prod-CsCWYmV7.mjs +293 -0
- package/dist/LexicalClipboard.prod-CsCWYmV7.mjs.map +1 -0
- package/dist/LexicalCodeCore.dev-haI2cqH8.mjs +539 -0
- package/dist/LexicalCodeCore.dev-haI2cqH8.mjs.map +1 -0
- package/dist/LexicalCodeCore.prod-D_k7zuE5.mjs +383 -0
- package/dist/LexicalCodeCore.prod-D_k7zuE5.mjs.map +1 -0
- package/dist/LexicalDragon.dev-CAggdFh_.mjs +90 -0
- package/dist/LexicalDragon.dev-CAggdFh_.mjs.map +1 -0
- package/dist/LexicalDragon.node-QMR_wqJg.mjs +15 -0
- package/dist/LexicalDragon.node-QMR_wqJg.mjs.map +1 -0
- package/dist/LexicalDragon.prod-OssQhwS-.mjs +61 -0
- package/dist/LexicalDragon.prod-OssQhwS-.mjs.map +1 -0
- package/dist/LexicalExtension.dev-CtDZiMCU.mjs +1665 -0
- package/dist/LexicalExtension.dev-CtDZiMCU.mjs.map +1 -0
- package/dist/LexicalExtension.node-BM0gh8zu.mjs +51 -0
- package/dist/LexicalExtension.node-BM0gh8zu.mjs.map +1 -0
- package/dist/LexicalExtension.prod-Ug6emhEO.mjs +971 -0
- package/dist/LexicalExtension.prod-Ug6emhEO.mjs.map +1 -0
- package/dist/LexicalHeadless.dev-D4yQpl_s.mjs +45 -0
- package/dist/LexicalHeadless.dev-D4yQpl_s.mjs.map +1 -0
- package/dist/LexicalHeadless.prod-jeN65B3k.mjs +31 -0
- package/dist/LexicalHeadless.prod-jeN65B3k.mjs.map +1 -0
- package/dist/LexicalHtml.dev-BdU1_ITQ.mjs +686 -0
- package/dist/LexicalHtml.dev-BdU1_ITQ.mjs.map +1 -0
- package/dist/LexicalHtml.node-DLtAh00E.mjs +26 -0
- package/dist/LexicalHtml.node-DLtAh00E.mjs.map +1 -0
- package/dist/LexicalHtml.prod-DdLE1HHT.mjs +432 -0
- package/dist/LexicalHtml.prod-DdLE1HHT.mjs.map +1 -0
- package/dist/LexicalLink.dev-DK87Biuy.mjs +1084 -0
- package/dist/LexicalLink.dev-DK87Biuy.mjs.map +1 -0
- package/dist/LexicalLink.prod-fssuv6Sf.mjs +717 -0
- package/dist/LexicalLink.prod-fssuv6Sf.mjs.map +1 -0
- package/dist/LexicalList.dev-BJ4dr_rk.mjs +1352 -0
- package/dist/LexicalList.dev-BJ4dr_rk.mjs.map +1 -0
- package/dist/LexicalList.prod-jWBbaWfi.mjs +841 -0
- package/dist/LexicalList.prod-jWBbaWfi.mjs.map +1 -0
- package/dist/LexicalMarkdown.dev-C8e-eh6y.mjs +1207 -0
- package/dist/LexicalMarkdown.dev-C8e-eh6y.mjs.map +1 -0
- package/dist/LexicalMarkdown.prod-k73tZVb2.mjs +756 -0
- package/dist/LexicalMarkdown.prod-k73tZVb2.mjs.map +1 -0
- package/dist/LexicalRichText.dev-CrSqq3K-.mjs +645 -0
- package/dist/LexicalRichText.dev-CrSqq3K-.mjs.map +1 -0
- package/dist/LexicalRichText.node-B7k7wlQz.mjs +106 -0
- package/dist/LexicalRichText.node-B7k7wlQz.mjs.map +1 -0
- package/dist/LexicalRichText.prod-CqvyxQ9m.mjs +429 -0
- package/dist/LexicalRichText.prod-CqvyxQ9m.mjs.map +1 -0
- package/dist/LexicalSelection.dev-sNOHKHe_.mjs +725 -0
- package/dist/LexicalSelection.dev-sNOHKHe_.mjs.map +1 -0
- package/dist/LexicalSelection.node-Di7GwBCZ.mjs +38 -0
- package/dist/LexicalSelection.node-Di7GwBCZ.mjs.map +1 -0
- package/dist/LexicalSelection.prod-SWllPJop.mjs +388 -0
- package/dist/LexicalSelection.prod-SWllPJop.mjs.map +1 -0
- package/dist/LexicalTable.dev-CQkLHXJx.mjs +3262 -0
- package/dist/LexicalTable.dev-CQkLHXJx.mjs.map +1 -0
- package/dist/LexicalTable.prod-BtqCwKSi.mjs +2218 -0
- package/dist/LexicalTable.prod-BtqCwKSi.mjs.map +1 -0
- package/dist/LexicalUtils.dev-DiUpsXE8.mjs +850 -0
- package/dist/LexicalUtils.dev-DiUpsXE8.mjs.map +1 -0
- package/dist/LexicalUtils.node-CmK462d1.mjs +66 -0
- package/dist/LexicalUtils.node-CmK462d1.mjs.map +1 -0
- package/dist/LexicalUtils.prod-bWwV4GAF.mjs +377 -0
- package/dist/LexicalUtils.prod-bWwV4GAF.mjs.map +1 -0
- package/dist/auth-DIFZzoMm.mjs +211 -0
- package/dist/auth-DIFZzoMm.mjs.map +1 -0
- package/dist/bin/mxs.d.mts +927 -0
- package/dist/bin/mxs.mjs +3249 -0
- package/dist/bin/mxs.mjs.map +1 -0
- package/dist/chunk-hT5z_Zn9.mjs +35 -0
- package/dist/config-store-DIu7x0ao.mjs +121 -0
- package/dist/config-store-DIu7x0ao.mjs.map +1 -0
- package/dist/create-B2AOQL1e.mjs +24 -0
- package/dist/create-B2AOQL1e.mjs.map +1 -0
- package/dist/create-CwjGcv2n.mjs +23 -0
- package/dist/create-CwjGcv2n.mjs.map +1 -0
- package/dist/create-DT1l2hue.mjs +23 -0
- package/dist/create-DT1l2hue.mjs.map +1 -0
- package/dist/create-DjlRHPXy.mjs +23 -0
- package/dist/create-DjlRHPXy.mjs.map +1 -0
- package/dist/create-Dz6rJJSS.mjs +19 -0
- package/dist/create-Dz6rJJSS.mjs.map +1 -0
- package/dist/delete-B4dGrrus.mjs +22 -0
- package/dist/delete-B4dGrrus.mjs.map +1 -0
- package/dist/delete-BH6ZngTz.mjs +22 -0
- package/dist/delete-BH6ZngTz.mjs.map +1 -0
- package/dist/delete-BrY5zJ6S.mjs +22 -0
- package/dist/delete-BrY5zJ6S.mjs.map +1 -0
- package/dist/delete-CaG2d_xt.mjs +31 -0
- package/dist/delete-CaG2d_xt.mjs.map +1 -0
- package/dist/delete-YzRliRn8.mjs +31 -0
- package/dist/delete-YzRliRn8.mjs.map +1 -0
- package/dist/document-output-BVB8emLt.mjs +145 -0
- package/dist/document-output-BVB8emLt.mjs.map +1 -0
- package/dist/edit-Bb0Ze4w6.mjs +79 -0
- package/dist/edit-Bb0Ze4w6.mjs.map +1 -0
- package/dist/edit-BeFmC3XD.mjs +80 -0
- package/dist/edit-BeFmC3XD.mjs.map +1 -0
- package/dist/edit-DLDw_ZWX.mjs +86 -0
- package/dist/edit-DLDw_ZWX.mjs.map +1 -0
- package/dist/edit-z55ofr1S.mjs +40 -0
- package/dist/edit-z55ofr1S.mjs.map +1 -0
- package/dist/editor-xrF-j3UE.mjs +42 -0
- package/dist/editor-xrF-j3UE.mjs.map +1 -0
- package/dist/envelope-overlays-DPAlMNW1.mjs +26 -0
- package/dist/envelope-overlays-DPAlMNW1.mjs.map +1 -0
- package/dist/get-2qjq0wgG.mjs +28 -0
- package/dist/get-2qjq0wgG.mjs.map +1 -0
- package/dist/get-BGAk1Zmv.mjs +14 -0
- package/dist/get-BGAk1Zmv.mjs.map +1 -0
- package/dist/get-CRR13lgj.mjs +10 -0
- package/dist/get-CRR13lgj.mjs.map +1 -0
- package/dist/get-ClkpDBTL.mjs +13 -0
- package/dist/get-ClkpDBTL.mjs.map +1 -0
- package/dist/get-DJfUJ3sP.mjs +10 -0
- package/dist/get-DJfUJ3sP.mjs.map +1 -0
- package/dist/get-DiqaQvoS.mjs +14 -0
- package/dist/get-DiqaQvoS.mjs.map +1 -0
- package/dist/index.d.mts +338 -0
- package/dist/index.mjs +10 -0
- package/dist/list-BYzhflDg.mjs +14 -0
- package/dist/list-BYzhflDg.mjs.map +1 -0
- package/dist/list-DY-tPNG-.mjs +10 -0
- package/dist/list-DY-tPNG-.mjs.map +1 -0
- package/dist/list-DlQSkbqx.mjs +10 -0
- package/dist/list-DlQSkbqx.mjs.map +1 -0
- package/dist/list-DsrA5wIz.mjs +15 -0
- package/dist/list-DsrA5wIz.mjs.map +1 -0
- package/dist/list-Fp4nKfQb.mjs +10 -0
- package/dist/list-Fp4nKfQb.mjs.map +1 -0
- package/dist/list-s_tloiEX.mjs +10 -0
- package/dist/list-s_tloiEX.mjs.map +1 -0
- package/dist/litexml-codec-CDoRloiR.mjs +13451 -0
- package/dist/litexml-codec-CDoRloiR.mjs.map +1 -0
- package/dist/login-CLOJ-fTJ.mjs +579 -0
- package/dist/login-CLOJ-fTJ.mjs.map +1 -0
- package/dist/logout-v8ihxsim.mjs +11 -0
- package/dist/logout-v8ihxsim.mjs.map +1 -0
- package/dist/onboarding-640IldLz.mjs +888 -0
- package/dist/onboarding-640IldLz.mjs.map +1 -0
- package/dist/output-BM7aBFwt.mjs +120 -0
- package/dist/output-BM7aBFwt.mjs.map +1 -0
- package/dist/output-CTnO1lYt.d.mts +16 -0
- package/dist/payload-CN11pUto.mjs +547 -0
- package/dist/payload-CN11pUto.mjs.map +1 -0
- package/dist/publish-DkTYB-eK.mjs +23 -0
- package/dist/publish-DkTYB-eK.mjs.map +1 -0
- package/dist/publish-pRBfTJfe.mjs +23 -0
- package/dist/publish-pRBfTJfe.mjs.map +1 -0
- package/dist/resolve-BogJOR7R.mjs +119 -0
- package/dist/resolve-BogJOR7R.mjs.map +1 -0
- package/dist/resolve-Bp1AfBmM.mjs +26 -0
- package/dist/resolve-Bp1AfBmM.mjs.map +1 -0
- package/dist/resolve-Bw5Po9yK.mjs +16 -0
- package/dist/resolve-Bw5Po9yK.mjs.map +1 -0
- package/dist/resolve-CyjMlaoJ.mjs +23 -0
- package/dist/resolve-CyjMlaoJ.mjs.map +1 -0
- package/dist/resolve-helpers-CahPoJ0Y.mjs +30 -0
- package/dist/resolve-helpers-CahPoJ0Y.mjs.map +1 -0
- package/dist/set-BEGQz2si.mjs +43 -0
- package/dist/set-BEGQz2si.mjs.map +1 -0
- package/dist/shared-D0HVBDHI.mjs +39 -0
- package/dist/shared-D0HVBDHI.mjs.map +1 -0
- package/dist/status-LXrN9fI-.mjs +22 -0
- package/dist/status-LXrN9fI-.mjs.map +1 -0
- package/dist/update-B9ynFCdZ.mjs +30 -0
- package/dist/update-B9ynFCdZ.mjs.map +1 -0
- package/dist/update-BMZQPOvR.mjs +28 -0
- package/dist/update-BMZQPOvR.mjs.map +1 -0
- package/dist/update-BXz0l7Lz.mjs +36 -0
- package/dist/update-BXz0l7Lz.mjs.map +1 -0
- package/dist/update-Dx6AF8tf.mjs +30 -0
- package/dist/update-Dx6AF8tf.mjs.map +1 -0
- package/dist/update-q66TLS38.mjs +37 -0
- package/dist/update-q66TLS38.mjs.map +1 -0
- package/dist/whoami-CTkctOEd.mjs +22 -0
- package/dist/whoami-CTkctOEd.mjs.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { s as MxsError } from "./output-BM7aBFwt.mjs";
|
|
2
|
+
import { c as readCredentials, d as writeCredentials } from "./config-store-DIu7x0ao.mjs";
|
|
3
|
+
//#region src/core/auth.ts
|
|
4
|
+
const SUPPORTED_API_VERSIONS = [2];
|
|
5
|
+
function defaultHttp() {
|
|
6
|
+
return { fetch: globalThis.fetch.bind(globalThis) };
|
|
7
|
+
}
|
|
8
|
+
async function probeAuthEndpoint(apiUrl, http = defaultHttp()) {
|
|
9
|
+
const candidates = [...SUPPORTED_API_VERSIONS.map((v) => ({
|
|
10
|
+
prefix: `/api/v${v}/auth`,
|
|
11
|
+
version: v
|
|
12
|
+
})), {
|
|
13
|
+
prefix: "/auth",
|
|
14
|
+
version: null
|
|
15
|
+
}];
|
|
16
|
+
for (const cand of candidates) {
|
|
17
|
+
const probeUrl = `${apiUrl}${cand.prefix}/ok`;
|
|
18
|
+
let res;
|
|
19
|
+
try {
|
|
20
|
+
res = await http.fetch(probeUrl, { method: "GET" });
|
|
21
|
+
} catch {
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if (res.ok) {
|
|
25
|
+
const apiVersion = cand.version ?? SUPPORTED_API_VERSIONS[0];
|
|
26
|
+
return {
|
|
27
|
+
apiUrl,
|
|
28
|
+
apiBase: cand.prefix === "/auth" ? apiUrl : `${apiUrl}/api/v${apiVersion}`,
|
|
29
|
+
authBase: `${apiUrl}${cand.prefix}`,
|
|
30
|
+
apiVersion
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
throw new MxsError({
|
|
35
|
+
code: "auth.probe",
|
|
36
|
+
message: "cannot detect auth endpoint",
|
|
37
|
+
hint: "verify the URL points at a running mx-core server"
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
async function requestDeviceCode(authBase, clientId, scope = "openid profile email", http = defaultHttp()) {
|
|
41
|
+
const res = await http.fetch(`${authBase}/device/code`, {
|
|
42
|
+
method: "POST",
|
|
43
|
+
headers: { "content-type": "application/json" },
|
|
44
|
+
body: JSON.stringify({
|
|
45
|
+
client_id: clientId,
|
|
46
|
+
scope
|
|
47
|
+
})
|
|
48
|
+
});
|
|
49
|
+
if (!res.ok) {
|
|
50
|
+
let body;
|
|
51
|
+
try {
|
|
52
|
+
body = await res.json();
|
|
53
|
+
} catch {
|
|
54
|
+
body = await res.text();
|
|
55
|
+
}
|
|
56
|
+
throw new MxsError({
|
|
57
|
+
code: "auth.denied",
|
|
58
|
+
message: `device code request failed (${res.status})`,
|
|
59
|
+
details: body
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
return await res.json();
|
|
63
|
+
}
|
|
64
|
+
async function pollDeviceToken(authBase, clientId, deviceCode, opts) {
|
|
65
|
+
const http = opts.http ?? defaultHttp();
|
|
66
|
+
const deadline = Date.now() + opts.expiresInSec * 1e3;
|
|
67
|
+
let intervalMs = Math.max(1, opts.intervalSec) * 1e3;
|
|
68
|
+
while (Date.now() < deadline) {
|
|
69
|
+
if (opts.signal?.aborted) throw new MxsError({
|
|
70
|
+
code: "auth.denied",
|
|
71
|
+
message: "aborted by user"
|
|
72
|
+
});
|
|
73
|
+
await sleep(intervalMs, opts.signal);
|
|
74
|
+
const res = await http.fetch(`${authBase}/device/token`, {
|
|
75
|
+
method: "POST",
|
|
76
|
+
headers: { "content-type": "application/json" },
|
|
77
|
+
body: JSON.stringify({
|
|
78
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
79
|
+
device_code: deviceCode,
|
|
80
|
+
client_id: clientId
|
|
81
|
+
})
|
|
82
|
+
});
|
|
83
|
+
let body;
|
|
84
|
+
try {
|
|
85
|
+
body = await res.json();
|
|
86
|
+
} catch {
|
|
87
|
+
body = null;
|
|
88
|
+
}
|
|
89
|
+
if (res.ok && body?.access_token) return body;
|
|
90
|
+
const err = body?.error;
|
|
91
|
+
if (err === "authorization_pending") {
|
|
92
|
+
opts.onTick?.("pending");
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
if (err === "slow_down") {
|
|
96
|
+
intervalMs += 5e3;
|
|
97
|
+
opts.onTick?.("slow_down");
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (err === "access_denied") throw new MxsError({
|
|
101
|
+
code: "auth.denied",
|
|
102
|
+
message: "user denied the authorization request"
|
|
103
|
+
});
|
|
104
|
+
if (err === "expired_token") throw new MxsError({
|
|
105
|
+
code: "auth.expired",
|
|
106
|
+
message: "device code expired before authorization completed",
|
|
107
|
+
hint: "run `mxs auth login` again"
|
|
108
|
+
});
|
|
109
|
+
throw new MxsError({
|
|
110
|
+
code: "auth.denied",
|
|
111
|
+
message: `device token request failed (${res.status})`,
|
|
112
|
+
details: body
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
throw new MxsError({
|
|
116
|
+
code: "auth.expired",
|
|
117
|
+
message: "device authorization timed out",
|
|
118
|
+
hint: "run `mxs auth login` again"
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
function sleep(ms, signal) {
|
|
122
|
+
return new Promise((resolve, reject) => {
|
|
123
|
+
if (signal?.aborted) return reject(/* @__PURE__ */ new Error("aborted"));
|
|
124
|
+
const timer = setTimeout(() => {
|
|
125
|
+
if (signal) signal.removeEventListener("abort", onAbort);
|
|
126
|
+
resolve();
|
|
127
|
+
}, ms);
|
|
128
|
+
const onAbort = () => {
|
|
129
|
+
clearTimeout(timer);
|
|
130
|
+
reject(/* @__PURE__ */ new Error("aborted"));
|
|
131
|
+
};
|
|
132
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
function toCredentials(resp) {
|
|
136
|
+
return {
|
|
137
|
+
access_token: resp.access_token,
|
|
138
|
+
refresh_token: resp.refresh_token,
|
|
139
|
+
expires_at: Date.now() + resp.expires_in * 1e3,
|
|
140
|
+
user: resp.user
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function isExpiringSoon(cred, bufferMs = 6e4) {
|
|
144
|
+
return cred.expires_at < Date.now() + bufferMs;
|
|
145
|
+
}
|
|
146
|
+
async function refreshAccessToken(authBase, clientId, cred, http = defaultHttp()) {
|
|
147
|
+
if (!cred.refresh_token) return refreshSessionToken(authBase, cred, http);
|
|
148
|
+
const res = await http.fetch(`${authBase}/token`, {
|
|
149
|
+
method: "POST",
|
|
150
|
+
headers: { "content-type": "application/json" },
|
|
151
|
+
body: JSON.stringify({
|
|
152
|
+
grant_type: "refresh_token",
|
|
153
|
+
refresh_token: cred.refresh_token,
|
|
154
|
+
client_id: clientId
|
|
155
|
+
})
|
|
156
|
+
});
|
|
157
|
+
if (!res.ok) return null;
|
|
158
|
+
const body = await res.json();
|
|
159
|
+
if (!body?.access_token) return null;
|
|
160
|
+
return {
|
|
161
|
+
access_token: body.access_token,
|
|
162
|
+
refresh_token: body.refresh_token ?? cred.refresh_token,
|
|
163
|
+
expires_at: Date.now() + body.expires_in * 1e3,
|
|
164
|
+
user: body.user ?? cred.user
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
async function refreshSessionToken(authBase, cred, http) {
|
|
168
|
+
const res = await http.fetch(`${authBase}/get-session`, {
|
|
169
|
+
method: "GET",
|
|
170
|
+
headers: {
|
|
171
|
+
accept: "application/json",
|
|
172
|
+
authorization: `Bearer ${cred.access_token}`
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
if (!res.ok) return null;
|
|
176
|
+
const body = await res.json().catch(() => null);
|
|
177
|
+
const expiresAt = normalizeExpiresAt(body?.session?.expiresAt);
|
|
178
|
+
if (!expiresAt || expiresAt <= cred.expires_at) return null;
|
|
179
|
+
return {
|
|
180
|
+
access_token: res.headers.get("set-auth-token") ?? cred.access_token,
|
|
181
|
+
refresh_token: cred.refresh_token,
|
|
182
|
+
expires_at: expiresAt,
|
|
183
|
+
user: body?.user ?? cred.user
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function normalizeExpiresAt(value) {
|
|
187
|
+
if (value === void 0) return null;
|
|
188
|
+
const time = value instanceof Date ? value.getTime() : typeof value === "number" ? value : new Date(value).getTime();
|
|
189
|
+
return Number.isFinite(time) ? time : null;
|
|
190
|
+
}
|
|
191
|
+
async function loadCredentialsOrThrow() {
|
|
192
|
+
const cred = await readCredentials();
|
|
193
|
+
if (!cred) throw new MxsError({
|
|
194
|
+
code: "auth.missing",
|
|
195
|
+
message: "not authenticated",
|
|
196
|
+
hint: "run `mxs auth login`"
|
|
197
|
+
});
|
|
198
|
+
return cred;
|
|
199
|
+
}
|
|
200
|
+
async function ensureFreshToken(authBase, clientId, http = defaultHttp()) {
|
|
201
|
+
const cred = await loadCredentialsOrThrow();
|
|
202
|
+
if (!isExpiringSoon(cred)) return cred;
|
|
203
|
+
const refreshed = await refreshAccessToken(authBase, clientId, cred, http);
|
|
204
|
+
if (!refreshed) return cred;
|
|
205
|
+
await writeCredentials(refreshed);
|
|
206
|
+
return refreshed;
|
|
207
|
+
}
|
|
208
|
+
//#endregion
|
|
209
|
+
export { loadCredentialsOrThrow as a, refreshAccessToken as c, isExpiringSoon as i, requestDeviceCode as l, defaultHttp as n, pollDeviceToken as o, ensureFreshToken as r, probeAuthEndpoint as s, SUPPORTED_API_VERSIONS as t, toCredentials as u };
|
|
210
|
+
|
|
211
|
+
//# sourceMappingURL=auth-DIFZzoMm.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth-DIFZzoMm.mjs","names":[],"sources":["../src/core/auth.ts"],"sourcesContent":["import {\n type CredentialsShape,\n readCredentials,\n writeCredentials,\n} from './config-store'\nimport { MxsError } from './errors'\n\nexport const SUPPORTED_API_VERSIONS = [2] as const\n\nexport interface DeviceCodeResponse {\n device_code: string\n user_code: string\n verification_uri: string\n verification_uri_complete?: string\n expires_in: number\n interval: number\n}\n\nexport interface DeviceTokenResponse {\n access_token: string\n token_type: string\n expires_in: number\n refresh_token?: string\n scope?: string\n user?: {\n id?: string\n email?: string\n name?: string\n }\n}\n\nexport interface ProbeResult {\n apiUrl: string\n apiBase: string\n authBase: string\n apiVersion: number\n}\n\nexport interface AuthHttp {\n fetch: typeof fetch\n}\n\nexport function defaultHttp(): AuthHttp {\n return { fetch: globalThis.fetch.bind(globalThis) }\n}\n\nexport async function probeAuthEndpoint(\n apiUrl: string,\n http: AuthHttp = defaultHttp(),\n): Promise<ProbeResult> {\n const candidates: { prefix: string; version: number | null }[] = [\n ...SUPPORTED_API_VERSIONS.map((v) => ({\n prefix: `/api/v${v}/auth`,\n version: v,\n })),\n { prefix: '/auth', version: null },\n ]\n for (const cand of candidates) {\n const probeUrl = `${apiUrl}${cand.prefix}/ok`\n let res: Response\n try {\n res = await http.fetch(probeUrl, { method: 'GET' })\n } catch {\n continue\n }\n if (res.ok) {\n const apiVersion = cand.version ?? SUPPORTED_API_VERSIONS[0]\n const apiBase =\n cand.prefix === '/auth' ? apiUrl : `${apiUrl}/api/v${apiVersion}`\n const authBase = `${apiUrl}${cand.prefix}`\n return { apiUrl, apiBase, authBase, apiVersion }\n }\n }\n throw new MxsError({\n code: 'auth.probe',\n message: 'cannot detect auth endpoint',\n hint: 'verify the URL points at a running mx-core server',\n })\n}\n\nexport async function requestDeviceCode(\n authBase: string,\n clientId: string,\n scope = 'openid profile email',\n http: AuthHttp = defaultHttp(),\n): Promise<DeviceCodeResponse> {\n const res = await http.fetch(`${authBase}/device/code`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ client_id: clientId, scope }),\n })\n if (!res.ok) {\n let body: unknown\n try {\n body = await res.json()\n } catch {\n body = await res.text()\n }\n throw new MxsError({\n code: 'auth.denied',\n message: `device code request failed (${res.status})`,\n details: body,\n })\n }\n return (await res.json()) as DeviceCodeResponse\n}\n\nexport interface PollOptions {\n intervalSec: number\n expiresInSec: number\n signal?: AbortSignal\n onTick?: (state: 'pending' | 'slow_down') => void\n http?: AuthHttp\n}\n\nexport async function pollDeviceToken(\n authBase: string,\n clientId: string,\n deviceCode: string,\n opts: PollOptions,\n): Promise<DeviceTokenResponse> {\n const http = opts.http ?? defaultHttp()\n const deadline = Date.now() + opts.expiresInSec * 1000\n let intervalMs = Math.max(1, opts.intervalSec) * 1000\n while (Date.now() < deadline) {\n if (opts.signal?.aborted) {\n throw new MxsError({ code: 'auth.denied', message: 'aborted by user' })\n }\n await sleep(intervalMs, opts.signal)\n const res = await http.fetch(`${authBase}/device/token`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n grant_type: 'urn:ietf:params:oauth:grant-type:device_code',\n device_code: deviceCode,\n client_id: clientId,\n }),\n })\n let body: any\n try {\n body = await res.json()\n } catch {\n body = null\n }\n if (res.ok && body?.access_token) {\n return body as DeviceTokenResponse\n }\n const err = body?.error as string | undefined\n if (err === 'authorization_pending') {\n opts.onTick?.('pending')\n continue\n }\n if (err === 'slow_down') {\n intervalMs += 5000\n opts.onTick?.('slow_down')\n continue\n }\n if (err === 'access_denied') {\n throw new MxsError({\n code: 'auth.denied',\n message: 'user denied the authorization request',\n })\n }\n if (err === 'expired_token') {\n throw new MxsError({\n code: 'auth.expired',\n message: 'device code expired before authorization completed',\n hint: 'run `mxs auth login` again',\n })\n }\n throw new MxsError({\n code: 'auth.denied',\n message: `device token request failed (${res.status})`,\n details: body,\n })\n }\n throw new MxsError({\n code: 'auth.expired',\n message: 'device authorization timed out',\n hint: 'run `mxs auth login` again',\n })\n}\n\nfunction sleep(ms: number, signal?: AbortSignal): Promise<void> {\n return new Promise((resolve, reject) => {\n if (signal?.aborted) return reject(new Error('aborted'))\n const timer = setTimeout(() => {\n if (signal) signal.removeEventListener('abort', onAbort)\n resolve()\n }, ms)\n const onAbort = () => {\n clearTimeout(timer)\n reject(new Error('aborted'))\n }\n signal?.addEventListener('abort', onAbort, { once: true })\n })\n}\n\nexport function toCredentials(resp: DeviceTokenResponse): CredentialsShape {\n return {\n access_token: resp.access_token,\n refresh_token: resp.refresh_token,\n expires_at: Date.now() + resp.expires_in * 1000,\n user: resp.user,\n }\n}\n\nexport function isExpiringSoon(\n cred: CredentialsShape,\n bufferMs = 60_000,\n): boolean {\n return cred.expires_at < Date.now() + bufferMs\n}\n\nexport async function refreshAccessToken(\n authBase: string,\n clientId: string,\n cred: CredentialsShape,\n http: AuthHttp = defaultHttp(),\n): Promise<CredentialsShape | null> {\n if (!cred.refresh_token) {\n return refreshSessionToken(authBase, cred, http)\n }\n const res = await http.fetch(`${authBase}/token`, {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({\n grant_type: 'refresh_token',\n refresh_token: cred.refresh_token,\n client_id: clientId,\n }),\n })\n if (!res.ok) return null\n const body = (await res.json()) as DeviceTokenResponse\n if (!body?.access_token) return null\n return {\n access_token: body.access_token,\n refresh_token: body.refresh_token ?? cred.refresh_token,\n expires_at: Date.now() + body.expires_in * 1000,\n user: body.user ?? cred.user,\n }\n}\n\nasync function refreshSessionToken(\n authBase: string,\n cred: CredentialsShape,\n http: AuthHttp,\n): Promise<CredentialsShape | null> {\n const res = await http.fetch(`${authBase}/get-session`, {\n method: 'GET',\n headers: {\n accept: 'application/json',\n authorization: `Bearer ${cred.access_token}`,\n },\n })\n if (!res.ok) return null\n const body = (await res.json().catch(() => null)) as\n | {\n session?: { expiresAt?: string | Date | number }\n user?: CredentialsShape['user']\n }\n | null\n const expiresAt = normalizeExpiresAt(body?.session?.expiresAt)\n if (!expiresAt || expiresAt <= cred.expires_at) return null\n return {\n access_token: res.headers.get('set-auth-token') ?? cred.access_token,\n refresh_token: cred.refresh_token,\n expires_at: expiresAt,\n user: body?.user ?? cred.user,\n }\n}\n\nfunction normalizeExpiresAt(value: string | Date | number | undefined) {\n if (value === undefined) return null\n const time =\n value instanceof Date\n ? value.getTime()\n : typeof value === 'number'\n ? value\n : new Date(value).getTime()\n return Number.isFinite(time) ? time : null\n}\n\nexport async function loadCredentialsOrThrow(): Promise<CredentialsShape> {\n const cred = await readCredentials()\n if (!cred) {\n throw new MxsError({\n code: 'auth.missing',\n message: 'not authenticated',\n hint: 'run `mxs auth login`',\n })\n }\n return cred\n}\n\nexport async function ensureFreshToken(\n authBase: string,\n clientId: string,\n http: AuthHttp = defaultHttp(),\n): Promise<CredentialsShape> {\n const cred = await loadCredentialsOrThrow()\n if (!isExpiringSoon(cred)) return cred\n const refreshed = await refreshAccessToken(authBase, clientId, cred, http)\n if (!refreshed) return cred\n await writeCredentials(refreshed)\n return refreshed\n}\n"],"mappings":"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"}
|