perplexity-user-mcp 0.8.36
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 +192 -0
- package/dist/attachments.d.ts +20 -0
- package/dist/attachments.mjs +43 -0
- package/dist/checks/browser.d.ts +100 -0
- package/dist/checks/browser.mjs +89 -0
- package/dist/checks/config.d.ts +91 -0
- package/dist/checks/config.mjs +88 -0
- package/dist/checks/ide.d.ts +89 -0
- package/dist/checks/ide.mjs +80 -0
- package/dist/checks/mcp.d.ts +61 -0
- package/dist/checks/mcp.mjs +56 -0
- package/dist/checks/native-deps.d.ts +131 -0
- package/dist/checks/native-deps.mjs +115 -0
- package/dist/checks/network.d.ts +71 -0
- package/dist/checks/network.mjs +70 -0
- package/dist/checks/probe.d.ts +93 -0
- package/dist/checks/probe.mjs +82 -0
- package/dist/checks/profiles.d.ts +99 -0
- package/dist/checks/profiles.mjs +90 -0
- package/dist/checks/runtime.d.ts +89 -0
- package/dist/checks/runtime.mjs +90 -0
- package/dist/checks/vault.d.ts +101 -0
- package/dist/checks/vault.mjs +90 -0
- package/dist/chunk-3B276PGG.mjs +115 -0
- package/dist/chunk-4UEJOM6W.mjs +9 -0
- package/dist/chunk-6EP2BLTV.mjs +205 -0
- package/dist/chunk-6YMQVLFX.mjs +146 -0
- package/dist/chunk-7JL36EBH.mjs +118 -0
- package/dist/chunk-DPGMKSSA.mjs +57 -0
- package/dist/chunk-H4BUAPPO.mjs +1950 -0
- package/dist/chunk-HMKLWVXB.mjs +109 -0
- package/dist/chunk-HTUAQRKH.mjs +125 -0
- package/dist/chunk-HU5B4FXS.mjs +139 -0
- package/dist/chunk-KCXM2M4B.mjs +1006 -0
- package/dist/chunk-LKJMLGFP.mjs +237 -0
- package/dist/chunk-LZPLNZ5U.mjs +67 -0
- package/dist/chunk-MTDFKNXX.mjs +19 -0
- package/dist/chunk-OF4DMAPJ.mjs +511 -0
- package/dist/chunk-PE23RMXY.mjs +43 -0
- package/dist/chunk-Q2VY4R5F.mjs +175 -0
- package/dist/chunk-S5VD7WTU.mjs +2540 -0
- package/dist/chunk-SVPRB62V.mjs +106 -0
- package/dist/chunk-TQLCLE4L.mjs +345 -0
- package/dist/chunk-U3DGFLXZ.mjs +43 -0
- package/dist/chunk-X45O6YD3.mjs +688 -0
- package/dist/chunk-XKSWCEGI.mjs +168 -0
- package/dist/chunk-Z7DAACGZ.mjs +534 -0
- package/dist/chunk-ZQFUZPLO.mjs +257 -0
- package/dist/cli.d.ts +952 -0
- package/dist/cli.mjs +827 -0
- package/dist/client.d.ts +355 -0
- package/dist/client.mjs +27 -0
- package/dist/cloud-sync.d-Cqt6y18U.d.ts +42 -0
- package/dist/cloud-sync.d.ts +42 -0
- package/dist/cloud-sync.mjs +17 -0
- package/dist/config.d.ts +186 -0
- package/dist/config.mjs +54 -0
- package/dist/daemon/attach.d.ts +36 -0
- package/dist/daemon/attach.mjs +25 -0
- package/dist/daemon/audit.d.ts +23 -0
- package/dist/daemon/audit.mjs +12 -0
- package/dist/daemon/client-http.d.ts +42 -0
- package/dist/daemon/client-http.mjs +29 -0
- package/dist/daemon/index.d.ts +14 -0
- package/dist/daemon/index.mjs +110 -0
- package/dist/daemon/install-tunnel.d.ts +46 -0
- package/dist/daemon/install-tunnel.mjs +14 -0
- package/dist/daemon/launcher.d.ts +163 -0
- package/dist/daemon/launcher.mjs +50 -0
- package/dist/daemon/lockfile.d.ts +29 -0
- package/dist/daemon/lockfile.mjs +18 -0
- package/dist/daemon/server.d.ts +159 -0
- package/dist/daemon/server.mjs +20 -0
- package/dist/daemon/token.d.ts +17 -0
- package/dist/daemon/token.mjs +17 -0
- package/dist/daemon/tunnel-providers/index.d.ts +330 -0
- package/dist/daemon/tunnel-providers/index.mjs +57 -0
- package/dist/daemon/tunnel.d.ts +23 -0
- package/dist/daemon/tunnel.mjs +9 -0
- package/dist/doctor-report.d.ts +24 -0
- package/dist/doctor-report.mjs +14 -0
- package/dist/doctor.d-CXmUqOXX.d.ts +43 -0
- package/dist/doctor.d.ts +44 -0
- package/dist/doctor.mjs +16 -0
- package/dist/export.d.ts +19 -0
- package/dist/export.mjs +15 -0
- package/dist/health-check.d.ts +108 -0
- package/dist/health-check.mjs +92 -0
- package/dist/history-store.d-BzjBF2m3.d.ts +65 -0
- package/dist/history-store.d.ts +65 -0
- package/dist/history-store.mjs +48 -0
- package/dist/impit-login-runner.d.ts +469 -0
- package/dist/impit-login-runner.mjs +685 -0
- package/dist/index.d.ts +159 -0
- package/dist/index.mjs +236 -0
- package/dist/login-runner.d.ts +333 -0
- package/dist/login-runner.mjs +320 -0
- package/dist/logout.d.ts +28 -0
- package/dist/logout.mjs +45 -0
- package/dist/manual-login-runner.d.ts +150 -0
- package/dist/manual-login-runner.mjs +146 -0
- package/dist/native-deps-BNThFHxa.d.ts +175 -0
- package/dist/native-deps-YNKXITRY.mjs +139 -0
- package/dist/profiles.d-DqS1oZWr.d.ts +41 -0
- package/dist/profiles.d.ts +41 -0
- package/dist/profiles.mjs +33 -0
- package/dist/redact.d.ts +159 -0
- package/dist/redact.mjs +11 -0
- package/dist/refresh.d.ts +118 -0
- package/dist/refresh.mjs +21 -0
- package/dist/reinit-watcher.d.ts +15 -0
- package/dist/reinit-watcher.mjs +8 -0
- package/dist/session-metadata-B9aV_n5g.d.ts +148 -0
- package/dist/tty-prompt.d.ts +44 -0
- package/dist/tty-prompt.mjs +39 -0
- package/dist/vault.d-BtRSLZiM.d.ts +8 -0
- package/dist/vault.d.ts +37 -0
- package/dist/vault.mjs +21 -0
- package/dist/viewer-detect.d-HWGnyFAA.d.ts +4 -0
- package/dist/viewer-detect.d.ts +4 -0
- package/dist/viewer-detect.mjs +37 -0
- package/dist/viewers.d-BGCK6sw6.d.ts +10 -0
- package/dist/viewers.d.ts +18 -0
- package/dist/viewers.mjs +122 -0
- package/package.json +152 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// src/redact.js
|
|
2
|
+
var SECRET_PATTERNS = Object.freeze([
|
|
3
|
+
// OAuth / local prefixes come FIRST so a value like `"bearer":"pplx_at_…"`
|
|
4
|
+
// gets the specific oauth-access tag instead of the generic daemon-bearer
|
|
5
|
+
// one from the bearer-json catchall below.
|
|
6
|
+
{ name: "oauth-access", kind: "oauth-access", re: /pplx_at_[A-Za-z0-9_\-]{10,}/g },
|
|
7
|
+
{ name: "oauth-refresh", kind: "oauth-refresh", re: /pplx_rt_[A-Za-z0-9_\-]{10,}/g },
|
|
8
|
+
{ name: "oauth-code", kind: "oauth-code", re: /pplx_ac_[A-Za-z0-9_\-]{10,}/g },
|
|
9
|
+
{ name: "local-bearer", kind: "local-bearer", re: /pplx_local_[a-z0-9-]+_[A-Za-z0-9_\-]{10,}/g },
|
|
10
|
+
{ name: "daemon-bearer-json", kind: "daemon-bearer", re: /"bearerToken"\s*:\s*"[A-Za-z0-9_\-]{30,}"/g },
|
|
11
|
+
// Matches the `"bearer":"..."` shape used by daemon:bearer:reveal:response
|
|
12
|
+
// so reveal-payload logs stay leak-free. Value is only required to be
|
|
13
|
+
// 20+ safe-identifier chars — covers both raw daemon bearers and future
|
|
14
|
+
// short-lived tokens.
|
|
15
|
+
{ name: "bearer-json", kind: "daemon-bearer", re: /"bearer"\s*:\s*"[A-Za-z0-9_\-]{20,}"/g },
|
|
16
|
+
{ name: "authorization-header", kind: "bearer-header", re: /[Aa]uthorization\s*:\s*Bearer\s+[A-Za-z0-9_\-\.]{20,}/g },
|
|
17
|
+
{ name: "ngrok-authtoken", kind: "ngrok-authtoken", re: /"authtoken"\s*:\s*"\d+[A-Za-z0-9]{20,}"/g },
|
|
18
|
+
{ name: "jwt", kind: "jwt", re: /eyJ[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+\.[A-Za-z0-9_\-]+/g },
|
|
19
|
+
{ name: "cf-clearance", kind: "cf-clearance", re: /cf_clearance=[^;\s]+/g },
|
|
20
|
+
{ name: "perplexity-session", kind: "perplexity-session", re: /__Secure-next-auth\.session-token=[^;\s]+/g }
|
|
21
|
+
]);
|
|
22
|
+
function redactSecrets(input) {
|
|
23
|
+
if (typeof input !== "string") return input;
|
|
24
|
+
let out = input;
|
|
25
|
+
for (const { re, kind } of SECRET_PATTERNS) {
|
|
26
|
+
out = out.replace(re, (match) => {
|
|
27
|
+
if (match.startsWith('"bearerToken"')) return `"bearerToken":"<redacted:${kind}>"`;
|
|
28
|
+
if (match.startsWith('"bearer"')) return `"bearer":"<redacted:${kind}>"`;
|
|
29
|
+
if (/^[Aa]uthorization\s*:/i.test(match)) return match.replace(/Bearer\s+[A-Za-z0-9_\-\.]{20,}/, `Bearer <redacted:${kind}>`);
|
|
30
|
+
if (match.startsWith('"authtoken"')) return `"authtoken":"<redacted:${kind}>"`;
|
|
31
|
+
if (match.startsWith("cf_clearance=")) return `cf_clearance=<redacted:${kind}>`;
|
|
32
|
+
if (match.startsWith("__Secure-next-auth.session-token=")) return `__Secure-next-auth.session-token=<redacted:${kind}>`;
|
|
33
|
+
return `<redacted:${kind}>`;
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
return out;
|
|
37
|
+
}
|
|
38
|
+
var PATTERNS = [
|
|
39
|
+
// Emails: RFC 5322 subset. Must come before generic token rules because
|
|
40
|
+
// emails contain characters that other rules would catch.
|
|
41
|
+
{
|
|
42
|
+
re: /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g,
|
|
43
|
+
replace: "<email>"
|
|
44
|
+
},
|
|
45
|
+
// Perplexity user IDs: user_ followed by hex/alphanum (>=8 chars).
|
|
46
|
+
{
|
|
47
|
+
re: /\buser_[A-Fa-f0-9]{8,}\b/g,
|
|
48
|
+
replace: "<userId>"
|
|
49
|
+
},
|
|
50
|
+
// Home directory paths. Must replace before the "long opaque token" rule
|
|
51
|
+
// because long path segments would otherwise trip it.
|
|
52
|
+
{
|
|
53
|
+
re: /(\/Users\/|\/home\/)[^/\s]+/g,
|
|
54
|
+
replace: "<home>"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
re: /([A-Z]:)\\Users\\[^\\]+/g,
|
|
58
|
+
replace: "<home>"
|
|
59
|
+
},
|
|
60
|
+
// IPv4
|
|
61
|
+
{
|
|
62
|
+
re: /\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b/g,
|
|
63
|
+
replace: "<ip>"
|
|
64
|
+
},
|
|
65
|
+
// IPv6. Three alternations cover: (1) full 8-group addresses, (2) :: compressed
|
|
66
|
+
// forms with at least one prefix group (e.g. 2001:db8::1, fe80::1), (3) leading-::
|
|
67
|
+
// forms like ::1 and ::. A function filter then requires hex letters (a-f/A-F) OR
|
|
68
|
+
// a double-colon so that pure-digit colon-separated strings like "23:59:59"
|
|
69
|
+
// (HH:MM:SS wall-clock) and ISO timestamps are left untouched.
|
|
70
|
+
{
|
|
71
|
+
re: /\b(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\b|\b(?:[0-9a-fA-F]{1,4}:){1,7}:(?:[0-9a-fA-F]{1,4}(?::[0-9a-fA-F]{1,4}){0,6})?\b|(?<!\w)::(?:[0-9a-fA-F]{1,4}(?::[0-9a-fA-F]{1,4}){0,6})?(?!\w)/g,
|
|
72
|
+
replace: (match) => {
|
|
73
|
+
const hasHex = /[a-fA-F]/.test(match);
|
|
74
|
+
const hasDoubleColon = /::/.test(match);
|
|
75
|
+
return hasHex || hasDoubleColon ? "<ip>" : match;
|
|
76
|
+
}
|
|
77
|
+
},
|
|
78
|
+
// Long opaque tokens (base64 / hex, >=20 chars). Applied last so more
|
|
79
|
+
// specific rules win first. Captures key=value and replaces only the value.
|
|
80
|
+
{
|
|
81
|
+
re: /=([A-Za-z0-9+/=]{20,})/g,
|
|
82
|
+
replace: "=<redacted>"
|
|
83
|
+
}
|
|
84
|
+
];
|
|
85
|
+
function redact(value, _seen) {
|
|
86
|
+
if (value == null) return value;
|
|
87
|
+
if (typeof value === "string") return redactString(value);
|
|
88
|
+
if (typeof value !== "object") return value;
|
|
89
|
+
const seen = _seen ?? /* @__PURE__ */ new WeakSet();
|
|
90
|
+
if (seen.has(value)) return "<circular>";
|
|
91
|
+
seen.add(value);
|
|
92
|
+
if (Array.isArray(value)) return value.map((v) => redact(v, seen));
|
|
93
|
+
const out = {};
|
|
94
|
+
for (const [k, v] of Object.entries(value)) out[k] = redact(v, seen);
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
function redactString(s) {
|
|
98
|
+
let out = redactSecrets(s);
|
|
99
|
+
for (const { re, replace } of PATTERNS) {
|
|
100
|
+
out = out.replace(re, replace);
|
|
101
|
+
}
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export {
|
|
106
|
+
SECRET_PATTERNS,
|
|
107
|
+
redactSecrets,
|
|
108
|
+
redact
|
|
109
|
+
};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import {
|
|
2
|
+
safeAtomicWriteFileSync
|
|
3
|
+
} from "./chunk-MTDFKNXX.mjs";
|
|
4
|
+
import {
|
|
5
|
+
getConfigDir
|
|
6
|
+
} from "./chunk-XKSWCEGI.mjs";
|
|
7
|
+
|
|
8
|
+
// src/daemon/token.ts
|
|
9
|
+
import { randomBytes } from "crypto";
|
|
10
|
+
import { spawnSync } from "child_process";
|
|
11
|
+
import { chmodSync, existsSync, mkdirSync, readFileSync } from "fs";
|
|
12
|
+
import { dirname, join } from "path";
|
|
13
|
+
function getTokenPath(configDir = getConfigDir()) {
|
|
14
|
+
return join(configDir, "daemon.token");
|
|
15
|
+
}
|
|
16
|
+
function generateBearerToken() {
|
|
17
|
+
return randomBytes(32).toString("base64url");
|
|
18
|
+
}
|
|
19
|
+
function readToken(options = {}) {
|
|
20
|
+
const tokenPath = options.tokenPath ?? getTokenPath();
|
|
21
|
+
if (!existsSync(tokenPath)) {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
const parsed = JSON.parse(readFileSync(tokenPath, "utf8"));
|
|
25
|
+
return normalizeRecord(parsed);
|
|
26
|
+
}
|
|
27
|
+
function ensureToken(options = {}) {
|
|
28
|
+
const existing = readToken(options);
|
|
29
|
+
if (existing) {
|
|
30
|
+
return existing;
|
|
31
|
+
}
|
|
32
|
+
const now = (options.now ?? defaultNow)();
|
|
33
|
+
const record = {
|
|
34
|
+
bearerToken: generateBearerToken(),
|
|
35
|
+
version: 1,
|
|
36
|
+
createdAt: now,
|
|
37
|
+
rotatedAt: now
|
|
38
|
+
};
|
|
39
|
+
writeToken(record, options);
|
|
40
|
+
return record;
|
|
41
|
+
}
|
|
42
|
+
function rotateToken(options = {}) {
|
|
43
|
+
const previous = readToken(options);
|
|
44
|
+
const now = (options.now ?? defaultNow)();
|
|
45
|
+
const record = {
|
|
46
|
+
bearerToken: generateBearerToken(),
|
|
47
|
+
version: (previous?.version ?? 0) + 1,
|
|
48
|
+
createdAt: previous?.createdAt ?? now,
|
|
49
|
+
rotatedAt: now
|
|
50
|
+
};
|
|
51
|
+
writeToken(record, options);
|
|
52
|
+
return record;
|
|
53
|
+
}
|
|
54
|
+
function writeToken(record, options = {}) {
|
|
55
|
+
const tokenPath = options.tokenPath ?? getTokenPath();
|
|
56
|
+
const normalized = normalizeRecord(record);
|
|
57
|
+
mkdirSync(dirname(tokenPath), { recursive: true });
|
|
58
|
+
safeAtomicWriteFileSync(tokenPath, JSON.stringify(normalized, null, 2) + "\n", { encoding: "utf8", mode: 384 });
|
|
59
|
+
applyPrivatePermissions(tokenPath);
|
|
60
|
+
}
|
|
61
|
+
function normalizeRecord(value) {
|
|
62
|
+
if (!value || typeof value !== "object") {
|
|
63
|
+
throw new Error("Daemon token file must contain a JSON object.");
|
|
64
|
+
}
|
|
65
|
+
const record = value;
|
|
66
|
+
if (typeof record.bearerToken !== "string" || record.bearerToken.length === 0) {
|
|
67
|
+
throw new Error("Daemon token file field 'bearerToken' must be a non-empty string.");
|
|
68
|
+
}
|
|
69
|
+
if (!Number.isInteger(record.version) || Number(record.version) <= 0) {
|
|
70
|
+
throw new Error("Daemon token file field 'version' must be a positive integer.");
|
|
71
|
+
}
|
|
72
|
+
if (typeof record.createdAt !== "string" || record.createdAt.length === 0) {
|
|
73
|
+
throw new Error("Daemon token file field 'createdAt' must be a non-empty string.");
|
|
74
|
+
}
|
|
75
|
+
if (typeof record.rotatedAt !== "string" || record.rotatedAt.length === 0) {
|
|
76
|
+
throw new Error("Daemon token file field 'rotatedAt' must be a non-empty string.");
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
bearerToken: record.bearerToken,
|
|
80
|
+
version: Number(record.version),
|
|
81
|
+
createdAt: record.createdAt,
|
|
82
|
+
rotatedAt: record.rotatedAt
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function applyPrivatePermissions(tokenPath) {
|
|
86
|
+
if (process.platform === "win32") {
|
|
87
|
+
restrictWindowsAcl(tokenPath);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
chmodSync(tokenPath, 384);
|
|
91
|
+
}
|
|
92
|
+
function restrictWindowsAcl(tokenPath) {
|
|
93
|
+
const username = getWindowsUserName();
|
|
94
|
+
const grantTarget = `${username}:(R,W)`;
|
|
95
|
+
const result = spawnSync("icacls", [tokenPath, "/inheritance:r", "/grant:r", grantTarget], {
|
|
96
|
+
encoding: "utf8",
|
|
97
|
+
windowsHide: true
|
|
98
|
+
});
|
|
99
|
+
if (result.status !== 0) {
|
|
100
|
+
const detail = [result.stdout, result.stderr].filter(Boolean).join("\n").trim();
|
|
101
|
+
throw new Error(`Failed to restrict daemon token ACL via icacls.${detail ? ` ${detail}` : ""}`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function getWindowsUserName() {
|
|
105
|
+
const username = process.env.USERNAME;
|
|
106
|
+
const domain = process.env.USERDOMAIN;
|
|
107
|
+
if (domain && username) {
|
|
108
|
+
return `${domain}\\${username}`;
|
|
109
|
+
}
|
|
110
|
+
if (username) {
|
|
111
|
+
return username;
|
|
112
|
+
}
|
|
113
|
+
throw new Error("Unable to resolve Windows username for daemon token ACL.");
|
|
114
|
+
}
|
|
115
|
+
function defaultNow() {
|
|
116
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export {
|
|
120
|
+
getTokenPath,
|
|
121
|
+
generateBearerToken,
|
|
122
|
+
readToken,
|
|
123
|
+
ensureToken,
|
|
124
|
+
rotateToken
|
|
125
|
+
};
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// src/session-metadata.js
|
|
2
|
+
var API_VERSION_QUERY = "version=2.18&source=default";
|
|
3
|
+
function buildRuntimeEndpoints(origin) {
|
|
4
|
+
const base = origin.replace(/\/+$/, "");
|
|
5
|
+
return {
|
|
6
|
+
session: `${base}/api/auth/session?${API_VERSION_QUERY}`,
|
|
7
|
+
csrf: `${base}/api/auth/csrf?${API_VERSION_QUERY}`,
|
|
8
|
+
signInEmail: `${base}/api/auth/signin/email?${API_VERSION_QUERY}`,
|
|
9
|
+
otpRedirectLink: `${base}/api/auth/otp-redirect-link`,
|
|
10
|
+
ssoDetails: `${base}/rest/enterprise/organization/login/details?${API_VERSION_QUERY}`,
|
|
11
|
+
models: `${base}/rest/models/config?config_schema=v1&${API_VERSION_QUERY}`,
|
|
12
|
+
asi: `${base}/rest/billing/asi-access-decision?${API_VERSION_QUERY}`,
|
|
13
|
+
rateLimits: `${base}/rest/rate-limit/status?${API_VERSION_QUERY}`,
|
|
14
|
+
experiments: `${base}/rest/experiments/attributes?${API_VERSION_QUERY}`,
|
|
15
|
+
userInfo: `${base}/rest/user/info?${API_VERSION_QUERY}`
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
async function pageRequest(page, url, init = {}) {
|
|
19
|
+
return page.evaluate(async ({ url: target, init: requestInit }) => {
|
|
20
|
+
try {
|
|
21
|
+
const response = await fetch(target, {
|
|
22
|
+
credentials: "include",
|
|
23
|
+
...requestInit
|
|
24
|
+
});
|
|
25
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
26
|
+
let json = null;
|
|
27
|
+
let text = null;
|
|
28
|
+
try {
|
|
29
|
+
if (contentType.includes("json")) json = await response.json();
|
|
30
|
+
else text = (await response.text()).slice(0, 500);
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
return {
|
|
34
|
+
ok: response.ok,
|
|
35
|
+
status: response.status,
|
|
36
|
+
redirected: response.redirected,
|
|
37
|
+
url: response.url,
|
|
38
|
+
contentType,
|
|
39
|
+
json,
|
|
40
|
+
text
|
|
41
|
+
};
|
|
42
|
+
} catch (error) {
|
|
43
|
+
return {
|
|
44
|
+
ok: false,
|
|
45
|
+
status: 0,
|
|
46
|
+
redirected: false,
|
|
47
|
+
url: target,
|
|
48
|
+
contentType: "",
|
|
49
|
+
json: null,
|
|
50
|
+
text: null,
|
|
51
|
+
error: error?.message ?? String(error)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
}, { url, init });
|
|
55
|
+
}
|
|
56
|
+
async function pollSession(page, sessionUrl, { timeoutMs = 1e4, intervalMs = 500 } = {}) {
|
|
57
|
+
const started = Date.now();
|
|
58
|
+
while (Date.now() - started < timeoutMs) {
|
|
59
|
+
const sessionResp = await pageRequest(page, sessionUrl);
|
|
60
|
+
if (sessionResp.ok && sessionResp.json?.user?.id) {
|
|
61
|
+
return sessionResp.json;
|
|
62
|
+
}
|
|
63
|
+
await page.waitForTimeout(intervalMs);
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
function deriveAccountFlags({ experiments, userInfo, asi }) {
|
|
68
|
+
const isEnterprise = userInfo?.is_enterprise === true || experiments?.server_is_enterprise === true;
|
|
69
|
+
const isMax = experiments?.server_is_max === true;
|
|
70
|
+
const canUseComputer = asi?.can_use_computer ?? false;
|
|
71
|
+
const isPro = experiments?.server_is_pro === true || canUseComputer && !isMax && !isEnterprise;
|
|
72
|
+
return { isPro, isMax, isEnterprise, canUseComputer };
|
|
73
|
+
}
|
|
74
|
+
function deriveTier(payload) {
|
|
75
|
+
const { isPro, isMax, isEnterprise } = deriveAccountFlags(payload);
|
|
76
|
+
if (isMax) return "Max";
|
|
77
|
+
if (isEnterprise) return "Enterprise";
|
|
78
|
+
if (isPro) return "Pro";
|
|
79
|
+
return "Authenticated";
|
|
80
|
+
}
|
|
81
|
+
async function collectSessionMetadata(page, origin, opts = {}) {
|
|
82
|
+
const endpoints = buildRuntimeEndpoints(origin);
|
|
83
|
+
const sessionData = opts.sessionData ?? await pollSession(page, endpoints.session, { timeoutMs: opts.sessionTimeoutMs ?? 1e4 });
|
|
84
|
+
if (!sessionData?.user?.id) {
|
|
85
|
+
return {
|
|
86
|
+
sessionData: null,
|
|
87
|
+
models: null,
|
|
88
|
+
asi: null,
|
|
89
|
+
rateLimits: null,
|
|
90
|
+
experiments: null,
|
|
91
|
+
userInfo: null,
|
|
92
|
+
tier: "Authenticated",
|
|
93
|
+
cache: {
|
|
94
|
+
modelsConfig: null,
|
|
95
|
+
rateLimits: null,
|
|
96
|
+
isPro: false,
|
|
97
|
+
isMax: false,
|
|
98
|
+
isEnterprise: false,
|
|
99
|
+
canUseComputer: false
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
const [modelsResp, asiResp, rateResp, expResp, userInfoResp] = await Promise.all([
|
|
104
|
+
pageRequest(page, endpoints.models),
|
|
105
|
+
pageRequest(page, endpoints.asi),
|
|
106
|
+
pageRequest(page, endpoints.rateLimits),
|
|
107
|
+
pageRequest(page, endpoints.experiments),
|
|
108
|
+
pageRequest(page, endpoints.userInfo)
|
|
109
|
+
]);
|
|
110
|
+
const payload = {
|
|
111
|
+
experiments: expResp.ok ? expResp.json : null,
|
|
112
|
+
userInfo: userInfoResp.ok ? userInfoResp.json : null,
|
|
113
|
+
asi: asiResp.ok ? asiResp.json : null
|
|
114
|
+
};
|
|
115
|
+
const flags = deriveAccountFlags(payload);
|
|
116
|
+
return {
|
|
117
|
+
sessionData,
|
|
118
|
+
models: modelsResp.ok ? modelsResp.json : null,
|
|
119
|
+
asi: asiResp.ok ? asiResp.json : null,
|
|
120
|
+
rateLimits: rateResp.ok ? rateResp.json : null,
|
|
121
|
+
experiments: expResp.ok ? expResp.json : null,
|
|
122
|
+
userInfo: userInfoResp.ok ? userInfoResp.json : null,
|
|
123
|
+
tier: deriveTier(payload),
|
|
124
|
+
cache: {
|
|
125
|
+
modelsConfig: modelsResp.ok ? modelsResp.json : null,
|
|
126
|
+
rateLimits: rateResp.ok ? rateResp.json : null,
|
|
127
|
+
isPro: flags.isPro,
|
|
128
|
+
isMax: flags.isMax,
|
|
129
|
+
isEnterprise: flags.isEnterprise,
|
|
130
|
+
canUseComputer: flags.canUseComputer
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export {
|
|
136
|
+
buildRuntimeEndpoints,
|
|
137
|
+
pageRequest,
|
|
138
|
+
collectSessionMetadata
|
|
139
|
+
};
|