@tickernelz/paperclip-pro-adapter-grok-local 2026.925.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 +22 -0
- package/dist/cli/format-event.d.ts +2 -0
- package/dist/cli/format-event.d.ts.map +1 -0
- package/dist/cli/format-event.js +55 -0
- package/dist/cli/format-event.js.map +1 -0
- package/dist/cli/format-event.test.d.ts +2 -0
- package/dist/cli/format-event.test.d.ts.map +1 -0
- package/dist/cli/format-event.test.js +19 -0
- package/dist/cli/format-event.test.js.map +1 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/server/adapter-auth-promotion.d.ts +87 -0
- package/dist/server/adapter-auth-promotion.d.ts.map +1 -0
- package/dist/server/adapter-auth-promotion.js +294 -0
- package/dist/server/adapter-auth-promotion.js.map +1 -0
- package/dist/server/adapter-auth-promotion.test.d.ts +2 -0
- package/dist/server/adapter-auth-promotion.test.d.ts.map +1 -0
- package/dist/server/adapter-auth-promotion.test.js +563 -0
- package/dist/server/adapter-auth-promotion.test.js.map +1 -0
- package/dist/server/device-login-parse.d.ts +23 -0
- package/dist/server/device-login-parse.d.ts.map +1 -0
- package/dist/server/device-login-parse.js +148 -0
- package/dist/server/device-login-parse.js.map +1 -0
- package/dist/server/device-login-parse.test.d.ts +2 -0
- package/dist/server/device-login-parse.test.d.ts.map +1 -0
- package/dist/server/device-login-parse.test.js +146 -0
- package/dist/server/device-login-parse.test.js.map +1 -0
- package/dist/server/execute.d.ts +3 -0
- package/dist/server/execute.d.ts.map +1 -0
- package/dist/server/execute.js +572 -0
- package/dist/server/execute.js.map +1 -0
- package/dist/server/execute.test.d.ts +2 -0
- package/dist/server/execute.test.d.ts.map +1 -0
- package/dist/server/execute.test.js +731 -0
- package/dist/server/execute.test.js.map +1 -0
- package/dist/server/grok-auth-copyback.d.ts +48 -0
- package/dist/server/grok-auth-copyback.d.ts.map +1 -0
- package/dist/server/grok-auth-copyback.js +92 -0
- package/dist/server/grok-auth-copyback.js.map +1 -0
- package/dist/server/grok-auth-copyback.test.d.ts +2 -0
- package/dist/server/grok-auth-copyback.test.d.ts.map +1 -0
- package/dist/server/grok-auth-copyback.test.js +246 -0
- package/dist/server/grok-auth-copyback.test.js.map +1 -0
- package/dist/server/grok-auth-merge-decision.cjs +157 -0
- package/dist/server/grok-auth-merge-decision.d.ts +21 -0
- package/dist/server/grok-auth-merge-decision.d.ts.map +1 -0
- package/dist/server/grok-auth-merge-decision.js +58 -0
- package/dist/server/grok-auth-merge-decision.js.map +1 -0
- package/dist/server/grok-auth-merge-decision.test.d.ts +2 -0
- package/dist/server/grok-auth-merge-decision.test.d.ts.map +1 -0
- package/dist/server/grok-auth-merge-decision.test.js +189 -0
- package/dist/server/grok-auth-merge-decision.test.js.map +1 -0
- package/dist/server/grok-home.d.ts +57 -0
- package/dist/server/grok-home.d.ts.map +1 -0
- package/dist/server/grok-home.js +145 -0
- package/dist/server/grok-home.js.map +1 -0
- package/dist/server/grok-home.test.d.ts +2 -0
- package/dist/server/grok-home.test.d.ts.map +1 -0
- package/dist/server/grok-home.test.js +109 -0
- package/dist/server/grok-home.test.js.map +1 -0
- package/dist/server/index.d.ts +12 -0
- package/dist/server/index.d.ts.map +1 -0
- package/dist/server/index.js +67 -0
- package/dist/server/index.js.map +1 -0
- package/dist/server/parse.d.ts +15 -0
- package/dist/server/parse.d.ts.map +1 -0
- package/dist/server/parse.js +93 -0
- package/dist/server/parse.js.map +1 -0
- package/dist/server/parse.test.d.ts +2 -0
- package/dist/server/parse.test.d.ts.map +1 -0
- package/dist/server/parse.test.js +82 -0
- package/dist/server/parse.test.js.map +1 -0
- package/dist/server/skills.d.ts +4 -0
- package/dist/server/skills.d.ts.map +1 -0
- package/dist/server/skills.js +21 -0
- package/dist/server/skills.js.map +1 -0
- package/dist/server/test.d.ts +9 -0
- package/dist/server/test.d.ts.map +1 -0
- package/dist/server/test.js +362 -0
- package/dist/server/test.js.map +1 -0
- package/dist/server/test.test.d.ts +2 -0
- package/dist/server/test.test.d.ts.map +1 -0
- package/dist/server/test.test.js +293 -0
- package/dist/server/test.test.js.map +1 -0
- package/dist/shared/turn-boundary.d.ts +7 -0
- package/dist/shared/turn-boundary.d.ts.map +1 -0
- package/dist/shared/turn-boundary.js +44 -0
- package/dist/shared/turn-boundary.js.map +1 -0
- package/dist/shared/turn-boundary.test.d.ts +2 -0
- package/dist/shared/turn-boundary.test.d.ts.map +1 -0
- package/dist/shared/turn-boundary.test.js +43 -0
- package/dist/shared/turn-boundary.test.js.map +1 -0
- package/dist/ui/build-config.d.ts +3 -0
- package/dist/ui/build-config.d.ts.map +1 -0
- package/dist/ui/build-config.js +29 -0
- package/dist/ui/build-config.js.map +1 -0
- package/dist/ui/build-config.test.d.ts +2 -0
- package/dist/ui/build-config.test.d.ts.map +1 -0
- package/dist/ui/build-config.test.js +26 -0
- package/dist/ui/build-config.test.js.map +1 -0
- package/dist/ui/index.d.ts +3 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +3 -0
- package/dist/ui/index.js.map +1 -0
- package/dist/ui/parse-stdout.d.ts +7 -0
- package/dist/ui/parse-stdout.d.ts.map +1 -0
- package/dist/ui/parse-stdout.js +76 -0
- package/dist/ui/parse-stdout.js.map +1 -0
- package/dist/ui/parse-stdout.test.d.ts +2 -0
- package/dist/ui/parse-stdout.test.d.ts.map +1 -0
- package/dist/ui/parse-stdout.test.js +62 -0
- package/dist/ui/parse-stdout.test.js.map +1 -0
- package/package.json +58 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
// The device-login output parser. It reads the Grok `login --device-auth`
|
|
2
|
+
// output and returns the authorization URL and the one-time code, or null.
|
|
3
|
+
//
|
|
4
|
+
// Security (Control 1 — strict validation): the parser accepts only the exact
|
|
5
|
+
// origin `https://accounts.x.ai` and the exact path `/oauth2/device`, and it
|
|
6
|
+
// rejects any fragment, a different origin, or a different path. Unlike the
|
|
7
|
+
// Codex device-login URL, the Grok URL carries a query: the parser accepts
|
|
8
|
+
// exactly one query key, `user_code`, and rejects a repeated key or an extra
|
|
9
|
+
// key. It requires the `user_code` value to match the strict short-code
|
|
10
|
+
// pattern and to equal the code that stands alone on its own line. The parser
|
|
11
|
+
// returns the printed URL after the platform `URL` parser validates and
|
|
12
|
+
// normalizes it, so the output is never a raw, unvalidated token. The parser
|
|
13
|
+
// never logs the URL, the code, or any input byte, and it keeps them out of
|
|
14
|
+
// every thrown error. The parser is a pure function.
|
|
15
|
+
/** The one and only accepted device-login command. */
|
|
16
|
+
export const GROK_DEVICE_LOGIN_COMMAND = "grok login --device-auth";
|
|
17
|
+
/** The one and only accepted device-login URL origin. */
|
|
18
|
+
export const GROK_DEVICE_LOGIN_URL_ORIGIN = "https://accounts.x.ai";
|
|
19
|
+
/** The one and only accepted device-login URL path. */
|
|
20
|
+
export const GROK_DEVICE_LOGIN_URL_PATH = "/oauth2/device";
|
|
21
|
+
// Grok CLI wraps the URL and the code in ANSI color sequences on a later
|
|
22
|
+
// release, the same way Codex CLI 0.128.0 and later do. A color sequence is a
|
|
23
|
+
// Control Sequence Introducer (CSI): the ESC control byte, a `[`, zero or more
|
|
24
|
+
// parameter bytes (0x30-0x3F), zero or more intermediate bytes (0x20-0x2F), and
|
|
25
|
+
// one final byte (0x40-0x7E). The parser removes every CSI sequence first, so a
|
|
26
|
+
// colored URL or code reads the same as a plain one. The strip only normalizes
|
|
27
|
+
// the input; the URL and the code still pass the strict validation below.
|
|
28
|
+
// eslint-disable-next-line no-control-regex
|
|
29
|
+
const ANSI_CSI_RE = /\x1b\[[0-?]*[ -/]*[@-~]/g;
|
|
30
|
+
// A candidate URL token is a run of non-space characters that starts with an
|
|
31
|
+
// http or https scheme. The parser validates each candidate with the `URL`
|
|
32
|
+
// class; the regular expression only splits tokens out of the text.
|
|
33
|
+
const URL_TOKEN_RE = /https?:\/\/\S+/g;
|
|
34
|
+
// Trailing punctuation that prose commonly puts right after a URL. The parser
|
|
35
|
+
// strips only these characters. It never strips `?` or `#`, so a URL with a
|
|
36
|
+
// fragment stays malformed and the parser rejects it.
|
|
37
|
+
const TRAILING_PUNCTUATION_RE = /[)\].,;:!]+$/;
|
|
38
|
+
// The one-time code structure: four characters, a hyphen, then four
|
|
39
|
+
// characters, and nothing else. Every observed grok code matched this shape.
|
|
40
|
+
// The alphabet is not published, so the pattern binds the token class to
|
|
41
|
+
// alphanumerics rather than a fixed character set.
|
|
42
|
+
const CODE_PATTERN = /^[A-Za-z0-9]{4}-[A-Za-z0-9]{4}$/;
|
|
43
|
+
// The prompt line that introduces the one-time code, printed exactly this way.
|
|
44
|
+
const CODE_PREAMBLE = "Confirm this code in your browser:";
|
|
45
|
+
// The maximum number of characters between the end of the URL and the start of
|
|
46
|
+
// the code preamble. The captured prompt prints the preamble a few characters
|
|
47
|
+
// after the URL. The parser looks for the preamble only inside this window
|
|
48
|
+
// after the URL, so a preamble far away in the output cannot bind to the URL.
|
|
49
|
+
const MAX_URL_TO_PREAMBLE_GAP = 256;
|
|
50
|
+
// The maximum number of characters after the end of the preamble line that the
|
|
51
|
+
// parser reads for the code. The captured prompt prints the code on the line
|
|
52
|
+
// right after the preamble line.
|
|
53
|
+
const MAX_PREAMBLE_TO_CODE_GAP = 128;
|
|
54
|
+
/**
|
|
55
|
+
* Returns the validated device-login URL, the code its `user_code` query
|
|
56
|
+
* carries, and the end index, when `text` holds a standalone token with the
|
|
57
|
+
* exact origin {@link GROK_DEVICE_LOGIN_URL_ORIGIN} and the exact path
|
|
58
|
+
* {@link GROK_DEVICE_LOGIN_URL_PATH}, no fragment, no credentials, exactly one
|
|
59
|
+
* query key named `user_code`, and a `user_code` value that matches the strict
|
|
60
|
+
* short-code pattern. Returns null otherwise. Returns the platform `URL`
|
|
61
|
+
* parser's normalized form of the matched token, so the accepting rules bound
|
|
62
|
+
* the output even though it is not a fixed constant. The `end` index is the
|
|
63
|
+
* position of the first character after the matched token in `text`.
|
|
64
|
+
*/
|
|
65
|
+
function findExactDeviceUrl(text) {
|
|
66
|
+
for (const match of text.matchAll(URL_TOKEN_RE)) {
|
|
67
|
+
const token = match[0];
|
|
68
|
+
const cleaned = token.replace(TRAILING_PUNCTUATION_RE, "");
|
|
69
|
+
let parsed;
|
|
70
|
+
try {
|
|
71
|
+
parsed = new URL(cleaned);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (parsed.protocol !== "https:" ||
|
|
77
|
+
parsed.host !== "accounts.x.ai" ||
|
|
78
|
+
parsed.pathname !== GROK_DEVICE_LOGIN_URL_PATH ||
|
|
79
|
+
parsed.hash !== "" ||
|
|
80
|
+
parsed.username !== "" ||
|
|
81
|
+
parsed.password !== "") {
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
// Accept exactly one query key, `user_code`. Comparing the whole key list
|
|
85
|
+
// rejects a repeated key and an extra key alike.
|
|
86
|
+
const keys = Array.from(parsed.searchParams.keys());
|
|
87
|
+
if (keys.length !== 1 || keys[0] !== "user_code")
|
|
88
|
+
continue;
|
|
89
|
+
const code = parsed.searchParams.get("user_code");
|
|
90
|
+
if (!code || !CODE_PATTERN.test(code))
|
|
91
|
+
continue;
|
|
92
|
+
return { url: parsed.toString(), code, end: (match.index ?? 0) + token.length };
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Returns the one-time code when `text` holds the code preamble after
|
|
98
|
+
* `fromIndex` and a dedicated code line after that preamble line. Mirrors the
|
|
99
|
+
* Codex parser's proximity-bound search: the preamble must appear in a window
|
|
100
|
+
* after the URL, and the code must be the first non-blank line after the
|
|
101
|
+
* preamble line, trimmed, and hold nothing else. Returns null when the
|
|
102
|
+
* preamble is absent, when the preamble has no line break after it, or when
|
|
103
|
+
* the first non-blank line after the preamble line is not a code line.
|
|
104
|
+
*/
|
|
105
|
+
function findStandaloneCode(text, fromIndex) {
|
|
106
|
+
const preambleWindow = text.slice(fromIndex, fromIndex + MAX_URL_TO_PREAMBLE_GAP);
|
|
107
|
+
const preambleIndex = preambleWindow.indexOf(CODE_PREAMBLE);
|
|
108
|
+
if (preambleIndex === -1)
|
|
109
|
+
return null;
|
|
110
|
+
const preambleEnd = fromIndex + preambleIndex + CODE_PREAMBLE.length;
|
|
111
|
+
const lineBreak = text.indexOf("\n", preambleEnd);
|
|
112
|
+
if (lineBreak === -1)
|
|
113
|
+
return null;
|
|
114
|
+
const codeWindow = text.slice(lineBreak + 1, lineBreak + 1 + MAX_PREAMBLE_TO_CODE_GAP);
|
|
115
|
+
for (const line of codeWindow.split("\n")) {
|
|
116
|
+
const trimmed = line.trim();
|
|
117
|
+
if (trimmed.length === 0)
|
|
118
|
+
continue;
|
|
119
|
+
return CODE_PATTERN.test(trimmed) ? trimmed : null;
|
|
120
|
+
}
|
|
121
|
+
return null;
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Parses Grok device-login output. Removes ANSI color sequences first. The
|
|
125
|
+
* parser reads the URL first, validates its query and its embedded code, then
|
|
126
|
+
* finds the code preamble after the URL and reads the code from the dedicated
|
|
127
|
+
* code line right after the preamble line. It returns the prompt only when the
|
|
128
|
+
* code on that dedicated line equals the code the URL query carries. Returns
|
|
129
|
+
* null for any other input, including a non-string input, an absent prompt, a
|
|
130
|
+
* wrong origin or path, a URL with a fragment, a malformed or mismatched query,
|
|
131
|
+
* and a malformed short code. Never throws on input, and never puts the URL or
|
|
132
|
+
* the code into a log or an error.
|
|
133
|
+
*/
|
|
134
|
+
export function parseGrokDeviceLoginPrompt(text) {
|
|
135
|
+
if (typeof text !== "string" || text.length === 0)
|
|
136
|
+
return null;
|
|
137
|
+
const clean = text.replace(ANSI_CSI_RE, "");
|
|
138
|
+
const urlMatch = findExactDeviceUrl(clean);
|
|
139
|
+
if (!urlMatch)
|
|
140
|
+
return null;
|
|
141
|
+
const lineCode = findStandaloneCode(clean, urlMatch.end);
|
|
142
|
+
if (!lineCode)
|
|
143
|
+
return null;
|
|
144
|
+
if (lineCode !== urlMatch.code)
|
|
145
|
+
return null;
|
|
146
|
+
return { url: urlMatch.url, code: lineCode };
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=device-login-parse.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-login-parse.js","sourceRoot":"","sources":["../../src/server/device-login-parse.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-login-parse.test.d.ts","sourceRoot":"","sources":["../../src/server/device-login-parse.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
import { parseGrokDeviceLoginPrompt } from "./device-login-parse.js";
|
|
6
|
+
const fixturesDir = path.join(path.dirname(fileURLToPath(import.meta.url)), "__fixtures__");
|
|
7
|
+
function readFixture(name) {
|
|
8
|
+
return readFileSync(path.join(fixturesDir, name), "utf8");
|
|
9
|
+
}
|
|
10
|
+
const PREAMBLE = "Confirm this code in your browser:";
|
|
11
|
+
const ORIGIN = "https://accounts.x.ai";
|
|
12
|
+
const PATH = "/oauth2/device";
|
|
13
|
+
function buildPrompt(code, urlCode = code) {
|
|
14
|
+
return [
|
|
15
|
+
"",
|
|
16
|
+
"To sign in, open this URL in your browser:",
|
|
17
|
+
"",
|
|
18
|
+
` ${ORIGIN}${PATH}?user_code=${urlCode}`,
|
|
19
|
+
"",
|
|
20
|
+
PREAMBLE,
|
|
21
|
+
"",
|
|
22
|
+
` ${code}`,
|
|
23
|
+
"",
|
|
24
|
+
].join("\n");
|
|
25
|
+
}
|
|
26
|
+
describe("parseGrokDeviceLoginPrompt", () => {
|
|
27
|
+
it("parse_returns_url_and_code_from_the_captured_fixture", () => {
|
|
28
|
+
const result = parseGrokDeviceLoginPrompt(readFixture("device-login-prompt.txt"));
|
|
29
|
+
expect(result).not.toBeNull();
|
|
30
|
+
expect(result?.url).toBe(`${ORIGIN}${PATH}?user_code=XXXX-XXXX`);
|
|
31
|
+
expect(result?.code).toBe("XXXX-XXXX");
|
|
32
|
+
});
|
|
33
|
+
it("parse_returns_null_when_the_url_code_differs_from_the_standalone_code", () => {
|
|
34
|
+
const text = buildPrompt("ABCD-EFGH", "WXYZ-1234");
|
|
35
|
+
expect(parseGrokDeviceLoginPrompt(text)).toBeNull();
|
|
36
|
+
});
|
|
37
|
+
it("parse_returns_null_for_a_query_that_repeats_user_code", () => {
|
|
38
|
+
const text = [
|
|
39
|
+
"To sign in, open this URL in your browser:",
|
|
40
|
+
` ${ORIGIN}${PATH}?user_code=ABCD-EFGH&user_code=ABCD-EFGH`,
|
|
41
|
+
PREAMBLE,
|
|
42
|
+
" ABCD-EFGH",
|
|
43
|
+
].join("\n");
|
|
44
|
+
expect(parseGrokDeviceLoginPrompt(text)).toBeNull();
|
|
45
|
+
});
|
|
46
|
+
it("parse_returns_null_for_a_query_that_adds_a_second_key", () => {
|
|
47
|
+
const text = [
|
|
48
|
+
"To sign in, open this URL in your browser:",
|
|
49
|
+
` ${ORIGIN}${PATH}?user_code=ABCD-EFGH&session=1`,
|
|
50
|
+
PREAMBLE,
|
|
51
|
+
" ABCD-EFGH",
|
|
52
|
+
].join("\n");
|
|
53
|
+
expect(parseGrokDeviceLoginPrompt(text)).toBeNull();
|
|
54
|
+
});
|
|
55
|
+
it("parse_returns_null_for_a_wrong_origin", () => {
|
|
56
|
+
const text = [
|
|
57
|
+
"To sign in, open this URL in your browser:",
|
|
58
|
+
` https://accounts.example.com${PATH}?user_code=ABCD-EFGH`,
|
|
59
|
+
PREAMBLE,
|
|
60
|
+
" ABCD-EFGH",
|
|
61
|
+
].join("\n");
|
|
62
|
+
expect(parseGrokDeviceLoginPrompt(text)).toBeNull();
|
|
63
|
+
});
|
|
64
|
+
it("parse_returns_null_for_a_wrong_path", () => {
|
|
65
|
+
const text = [
|
|
66
|
+
"To sign in, open this URL in your browser:",
|
|
67
|
+
` ${ORIGIN}/oauth2/device/extra?user_code=ABCD-EFGH`,
|
|
68
|
+
PREAMBLE,
|
|
69
|
+
" ABCD-EFGH",
|
|
70
|
+
].join("\n");
|
|
71
|
+
expect(parseGrokDeviceLoginPrompt(text)).toBeNull();
|
|
72
|
+
});
|
|
73
|
+
it("parse_returns_null_for_a_url_with_a_fragment", () => {
|
|
74
|
+
const text = [
|
|
75
|
+
"To sign in, open this URL in your browser:",
|
|
76
|
+
` ${ORIGIN}${PATH}?user_code=ABCD-EFGH#section`,
|
|
77
|
+
PREAMBLE,
|
|
78
|
+
" ABCD-EFGH",
|
|
79
|
+
].join("\n");
|
|
80
|
+
expect(parseGrokDeviceLoginPrompt(text)).toBeNull();
|
|
81
|
+
});
|
|
82
|
+
it("parse_returns_null_for_a_five_character_code_group", () => {
|
|
83
|
+
const text = [
|
|
84
|
+
"To sign in, open this URL in your browser:",
|
|
85
|
+
` ${ORIGIN}${PATH}?user_code=ABCDE-FGHIJ`,
|
|
86
|
+
PREAMBLE,
|
|
87
|
+
" ABCDE-FGHIJ",
|
|
88
|
+
].join("\n");
|
|
89
|
+
expect(parseGrokDeviceLoginPrompt(text)).toBeNull();
|
|
90
|
+
});
|
|
91
|
+
it("parse_returns_null_when_the_prompt_is_absent", () => {
|
|
92
|
+
const text = "Some unrelated log line\nNothing to see here\n";
|
|
93
|
+
expect(parseGrokDeviceLoginPrompt(text)).toBeNull();
|
|
94
|
+
});
|
|
95
|
+
it("parse_reads_the_prompt_from_output_that_arrives_in_two_chunks", () => {
|
|
96
|
+
const full = buildPrompt("ABCD-EFGH");
|
|
97
|
+
const splitAt = Math.floor(full.length / 2);
|
|
98
|
+
const combined = full.slice(0, splitAt) + full.slice(splitAt);
|
|
99
|
+
const result = parseGrokDeviceLoginPrompt(combined);
|
|
100
|
+
expect(result).not.toBeNull();
|
|
101
|
+
expect(result?.code).toBe("ABCD-EFGH");
|
|
102
|
+
});
|
|
103
|
+
it("parse_reads_the_prompt_when_the_url_and_the_code_carry_ansi_sequences", () => {
|
|
104
|
+
const cyan = "\x1b[36m";
|
|
105
|
+
const bold = "\x1b[1m";
|
|
106
|
+
const reset = "\x1b[0m";
|
|
107
|
+
const text = [
|
|
108
|
+
"To sign in, open this URL in your browser:",
|
|
109
|
+
` ${cyan}${ORIGIN}${PATH}?user_code=ABCD-EFGH${reset}`,
|
|
110
|
+
PREAMBLE,
|
|
111
|
+
` ${bold}ABCD-EFGH${reset}`,
|
|
112
|
+
].join("\n");
|
|
113
|
+
const result = parseGrokDeviceLoginPrompt(text);
|
|
114
|
+
expect(result).not.toBeNull();
|
|
115
|
+
expect(result?.url).toBe(`${ORIGIN}${PATH}?user_code=ABCD-EFGH`);
|
|
116
|
+
expect(result?.code).toBe("ABCD-EFGH");
|
|
117
|
+
});
|
|
118
|
+
it("parse_reads_the_prompt_from_pipe_output_that_ends_every_line_with_a_line_feed_only", () => {
|
|
119
|
+
// The captured pipe transport ends every line with `\x0a` only, with no
|
|
120
|
+
// `\x0d`. The fixture already exercises this; this test pins the same
|
|
121
|
+
// shape with the exact captured line order and the two-space indent.
|
|
122
|
+
const text = "\n" +
|
|
123
|
+
"To sign in, open this URL in your browser:\n" +
|
|
124
|
+
"\n" +
|
|
125
|
+
` ${ORIGIN}${PATH}?user_code=ABCD-EFGH\n` +
|
|
126
|
+
"\n" +
|
|
127
|
+
`${PREAMBLE}\n` +
|
|
128
|
+
"\n" +
|
|
129
|
+
" ABCD-EFGH\n" +
|
|
130
|
+
"\n" +
|
|
131
|
+
"\x1b[90mOnly continue with a code you requested. Don't share it with anyone.\x1b[0m\n" +
|
|
132
|
+
"\n" +
|
|
133
|
+
"Waiting for authorization...\n";
|
|
134
|
+
const result = parseGrokDeviceLoginPrompt(text);
|
|
135
|
+
expect(result).not.toBeNull();
|
|
136
|
+
expect(result?.url).toBe(`${ORIGIN}${PATH}?user_code=ABCD-EFGH`);
|
|
137
|
+
expect(result?.code).toBe("ABCD-EFGH");
|
|
138
|
+
});
|
|
139
|
+
it("keeps the url and the code out of a thrown error", () => {
|
|
140
|
+
// @ts-expect-error deliberate wrong type
|
|
141
|
+
expect(parseGrokDeviceLoginPrompt(undefined)).toBeNull();
|
|
142
|
+
// @ts-expect-error deliberate wrong type
|
|
143
|
+
expect(parseGrokDeviceLoginPrompt(12345)).toBeNull();
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
//# sourceMappingURL=device-login-parse.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-login-parse.test.js","sourceRoot":"","sources":["../../src/server/device-login-parse.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../src/server/execute.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AAiM/G,wBAAsB,OAAO,CAAC,GAAG,EAAE,uBAAuB,GAAG,OAAO,CAAC,sBAAsB,CAAC,CA2f3F"}
|