@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.
Files changed (119) hide show
  1. package/LICENSE +22 -0
  2. package/dist/cli/format-event.d.ts +2 -0
  3. package/dist/cli/format-event.d.ts.map +1 -0
  4. package/dist/cli/format-event.js +55 -0
  5. package/dist/cli/format-event.js.map +1 -0
  6. package/dist/cli/format-event.test.d.ts +2 -0
  7. package/dist/cli/format-event.test.d.ts.map +1 -0
  8. package/dist/cli/format-event.test.js +19 -0
  9. package/dist/cli/format-event.test.js.map +1 -0
  10. package/dist/cli/index.d.ts +2 -0
  11. package/dist/cli/index.d.ts.map +1 -0
  12. package/dist/cli/index.js +2 -0
  13. package/dist/cli/index.js.map +1 -0
  14. package/dist/index.d.ts +10 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +53 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/server/adapter-auth-promotion.d.ts +87 -0
  19. package/dist/server/adapter-auth-promotion.d.ts.map +1 -0
  20. package/dist/server/adapter-auth-promotion.js +294 -0
  21. package/dist/server/adapter-auth-promotion.js.map +1 -0
  22. package/dist/server/adapter-auth-promotion.test.d.ts +2 -0
  23. package/dist/server/adapter-auth-promotion.test.d.ts.map +1 -0
  24. package/dist/server/adapter-auth-promotion.test.js +563 -0
  25. package/dist/server/adapter-auth-promotion.test.js.map +1 -0
  26. package/dist/server/device-login-parse.d.ts +23 -0
  27. package/dist/server/device-login-parse.d.ts.map +1 -0
  28. package/dist/server/device-login-parse.js +148 -0
  29. package/dist/server/device-login-parse.js.map +1 -0
  30. package/dist/server/device-login-parse.test.d.ts +2 -0
  31. package/dist/server/device-login-parse.test.d.ts.map +1 -0
  32. package/dist/server/device-login-parse.test.js +146 -0
  33. package/dist/server/device-login-parse.test.js.map +1 -0
  34. package/dist/server/execute.d.ts +3 -0
  35. package/dist/server/execute.d.ts.map +1 -0
  36. package/dist/server/execute.js +572 -0
  37. package/dist/server/execute.js.map +1 -0
  38. package/dist/server/execute.test.d.ts +2 -0
  39. package/dist/server/execute.test.d.ts.map +1 -0
  40. package/dist/server/execute.test.js +731 -0
  41. package/dist/server/execute.test.js.map +1 -0
  42. package/dist/server/grok-auth-copyback.d.ts +48 -0
  43. package/dist/server/grok-auth-copyback.d.ts.map +1 -0
  44. package/dist/server/grok-auth-copyback.js +92 -0
  45. package/dist/server/grok-auth-copyback.js.map +1 -0
  46. package/dist/server/grok-auth-copyback.test.d.ts +2 -0
  47. package/dist/server/grok-auth-copyback.test.d.ts.map +1 -0
  48. package/dist/server/grok-auth-copyback.test.js +246 -0
  49. package/dist/server/grok-auth-copyback.test.js.map +1 -0
  50. package/dist/server/grok-auth-merge-decision.cjs +157 -0
  51. package/dist/server/grok-auth-merge-decision.d.ts +21 -0
  52. package/dist/server/grok-auth-merge-decision.d.ts.map +1 -0
  53. package/dist/server/grok-auth-merge-decision.js +58 -0
  54. package/dist/server/grok-auth-merge-decision.js.map +1 -0
  55. package/dist/server/grok-auth-merge-decision.test.d.ts +2 -0
  56. package/dist/server/grok-auth-merge-decision.test.d.ts.map +1 -0
  57. package/dist/server/grok-auth-merge-decision.test.js +189 -0
  58. package/dist/server/grok-auth-merge-decision.test.js.map +1 -0
  59. package/dist/server/grok-home.d.ts +57 -0
  60. package/dist/server/grok-home.d.ts.map +1 -0
  61. package/dist/server/grok-home.js +145 -0
  62. package/dist/server/grok-home.js.map +1 -0
  63. package/dist/server/grok-home.test.d.ts +2 -0
  64. package/dist/server/grok-home.test.d.ts.map +1 -0
  65. package/dist/server/grok-home.test.js +109 -0
  66. package/dist/server/grok-home.test.js.map +1 -0
  67. package/dist/server/index.d.ts +12 -0
  68. package/dist/server/index.d.ts.map +1 -0
  69. package/dist/server/index.js +67 -0
  70. package/dist/server/index.js.map +1 -0
  71. package/dist/server/parse.d.ts +15 -0
  72. package/dist/server/parse.d.ts.map +1 -0
  73. package/dist/server/parse.js +93 -0
  74. package/dist/server/parse.js.map +1 -0
  75. package/dist/server/parse.test.d.ts +2 -0
  76. package/dist/server/parse.test.d.ts.map +1 -0
  77. package/dist/server/parse.test.js +82 -0
  78. package/dist/server/parse.test.js.map +1 -0
  79. package/dist/server/skills.d.ts +4 -0
  80. package/dist/server/skills.d.ts.map +1 -0
  81. package/dist/server/skills.js +21 -0
  82. package/dist/server/skills.js.map +1 -0
  83. package/dist/server/test.d.ts +9 -0
  84. package/dist/server/test.d.ts.map +1 -0
  85. package/dist/server/test.js +362 -0
  86. package/dist/server/test.js.map +1 -0
  87. package/dist/server/test.test.d.ts +2 -0
  88. package/dist/server/test.test.d.ts.map +1 -0
  89. package/dist/server/test.test.js +293 -0
  90. package/dist/server/test.test.js.map +1 -0
  91. package/dist/shared/turn-boundary.d.ts +7 -0
  92. package/dist/shared/turn-boundary.d.ts.map +1 -0
  93. package/dist/shared/turn-boundary.js +44 -0
  94. package/dist/shared/turn-boundary.js.map +1 -0
  95. package/dist/shared/turn-boundary.test.d.ts +2 -0
  96. package/dist/shared/turn-boundary.test.d.ts.map +1 -0
  97. package/dist/shared/turn-boundary.test.js +43 -0
  98. package/dist/shared/turn-boundary.test.js.map +1 -0
  99. package/dist/ui/build-config.d.ts +3 -0
  100. package/dist/ui/build-config.d.ts.map +1 -0
  101. package/dist/ui/build-config.js +29 -0
  102. package/dist/ui/build-config.js.map +1 -0
  103. package/dist/ui/build-config.test.d.ts +2 -0
  104. package/dist/ui/build-config.test.d.ts.map +1 -0
  105. package/dist/ui/build-config.test.js +26 -0
  106. package/dist/ui/build-config.test.js.map +1 -0
  107. package/dist/ui/index.d.ts +3 -0
  108. package/dist/ui/index.d.ts.map +1 -0
  109. package/dist/ui/index.js +3 -0
  110. package/dist/ui/index.js.map +1 -0
  111. package/dist/ui/parse-stdout.d.ts +7 -0
  112. package/dist/ui/parse-stdout.d.ts.map +1 -0
  113. package/dist/ui/parse-stdout.js +76 -0
  114. package/dist/ui/parse-stdout.js.map +1 -0
  115. package/dist/ui/parse-stdout.test.d.ts +2 -0
  116. package/dist/ui/parse-stdout.test.d.ts.map +1 -0
  117. package/dist/ui/parse-stdout.test.js +62 -0
  118. package/dist/ui/parse-stdout.test.js.map +1 -0
  119. 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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=device-login-parse.test.d.ts.map
@@ -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,3 @@
1
+ import type { AdapterExecutionContext, AdapterExecutionResult } from "@tickernelz/paperclip-pro-adapter-utils";
2
+ export declare function execute(ctx: AdapterExecutionContext): Promise<AdapterExecutionResult>;
3
+ //# sourceMappingURL=execute.d.ts.map
@@ -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"}