gologin-agent-browser-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.
Potentially problematic release.
This version of gologin-agent-browser-cli might be problematic. Click here for more details.
- package/LICENSE +21 -0
- package/README.md +220 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +352 -0
- package/dist/commands/check.d.ts +2 -0
- package/dist/commands/check.js +17 -0
- package/dist/commands/click.d.ts +2 -0
- package/dist/commands/click.js +17 -0
- package/dist/commands/close.d.ts +2 -0
- package/dist/commands/close.js +12 -0
- package/dist/commands/current.d.ts +2 -0
- package/dist/commands/current.js +9 -0
- package/dist/commands/dblclick.d.ts +2 -0
- package/dist/commands/dblclick.js +17 -0
- package/dist/commands/fill.d.ts +2 -0
- package/dist/commands/fill.js +18 -0
- package/dist/commands/find.d.ts +2 -0
- package/dist/commands/find.js +86 -0
- package/dist/commands/focus.d.ts +2 -0
- package/dist/commands/focus.js +17 -0
- package/dist/commands/get.d.ts +2 -0
- package/dist/commands/get.js +19 -0
- package/dist/commands/hover.d.ts +2 -0
- package/dist/commands/hover.js +17 -0
- package/dist/commands/open.d.ts +2 -0
- package/dist/commands/open.js +67 -0
- package/dist/commands/pdf.d.ts +2 -0
- package/dist/commands/pdf.js +18 -0
- package/dist/commands/press.d.ts +2 -0
- package/dist/commands/press.js +19 -0
- package/dist/commands/screenshot.d.ts +2 -0
- package/dist/commands/screenshot.js +20 -0
- package/dist/commands/scroll.d.ts +2 -0
- package/dist/commands/scroll.js +25 -0
- package/dist/commands/scrollIntoView.d.ts +2 -0
- package/dist/commands/scrollIntoView.js +17 -0
- package/dist/commands/select.d.ts +2 -0
- package/dist/commands/select.js +18 -0
- package/dist/commands/sessions.d.ts +2 -0
- package/dist/commands/sessions.js +15 -0
- package/dist/commands/shared.d.ts +3 -0
- package/dist/commands/shared.js +13 -0
- package/dist/commands/snapshot.d.ts +2 -0
- package/dist/commands/snapshot.js +16 -0
- package/dist/commands/type.d.ts +2 -0
- package/dist/commands/type.js +18 -0
- package/dist/commands/uncheck.d.ts +2 -0
- package/dist/commands/uncheck.js +17 -0
- package/dist/commands/upload.d.ts +2 -0
- package/dist/commands/upload.js +18 -0
- package/dist/commands/wait.d.ts +2 -0
- package/dist/commands/wait.js +41 -0
- package/dist/daemon/browser.d.ts +37 -0
- package/dist/daemon/browser.js +557 -0
- package/dist/daemon/refStore.d.ts +9 -0
- package/dist/daemon/refStore.js +26 -0
- package/dist/daemon/server.d.ts +1 -0
- package/dist/daemon/server.js +235 -0
- package/dist/daemon/sessionManager.d.ts +50 -0
- package/dist/daemon/sessionManager.js +512 -0
- package/dist/daemon/snapshot.d.ts +8 -0
- package/dist/daemon/snapshot.js +285 -0
- package/dist/lib/config.d.ts +3 -0
- package/dist/lib/config.js +58 -0
- package/dist/lib/errors.d.ts +12 -0
- package/dist/lib/errors.js +63 -0
- package/dist/lib/types.d.ts +301 -0
- package/dist/lib/types.js +2 -0
- package/dist/lib/utils.d.ts +27 -0
- package/dist/lib/utils.js +165 -0
- package/examples/agent-flow.sh +19 -0
- package/package.json +59 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildSnapshotModel = buildSnapshotModel;
|
|
4
|
+
exports.buildSnapshot = buildSnapshot;
|
|
5
|
+
const INTERACTIVE_KINDS = new Set([
|
|
6
|
+
"link",
|
|
7
|
+
"button",
|
|
8
|
+
"input",
|
|
9
|
+
"checkbox",
|
|
10
|
+
"radio",
|
|
11
|
+
"textarea",
|
|
12
|
+
"select"
|
|
13
|
+
]);
|
|
14
|
+
function normalizeWhitespace(value) {
|
|
15
|
+
if (!value) {
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
const normalized = value.replace(/\s+/g, " ").trim();
|
|
19
|
+
return normalized.length > 0 ? normalized : undefined;
|
|
20
|
+
}
|
|
21
|
+
function buildCandidateKey(candidate) {
|
|
22
|
+
return [
|
|
23
|
+
candidate.kind,
|
|
24
|
+
candidate.tag,
|
|
25
|
+
candidate.role ?? "",
|
|
26
|
+
normalizeWhitespace(candidate.accessibleName) ?? "",
|
|
27
|
+
normalizeWhitespace(candidate.text) ?? "",
|
|
28
|
+
normalizeWhitespace(candidate.ariaLabel) ?? "",
|
|
29
|
+
normalizeWhitespace(candidate.placeholder) ?? "",
|
|
30
|
+
normalizeWhitespace(candidate.name) ?? "",
|
|
31
|
+
normalizeWhitespace(candidate.href) ?? "",
|
|
32
|
+
normalizeWhitespace(candidate.inputType) ?? ""
|
|
33
|
+
].join("|");
|
|
34
|
+
}
|
|
35
|
+
function candidateText(candidate) {
|
|
36
|
+
return (normalizeWhitespace(candidate.accessibleName) ??
|
|
37
|
+
normalizeWhitespace(candidate.text) ??
|
|
38
|
+
normalizeWhitespace(candidate.selectedText) ??
|
|
39
|
+
normalizeWhitespace(candidate.placeholder) ??
|
|
40
|
+
normalizeWhitespace(candidate.name) ??
|
|
41
|
+
normalizeWhitespace(candidate.href));
|
|
42
|
+
}
|
|
43
|
+
function buildSnapshotModel(rawCandidates, options = {}) {
|
|
44
|
+
const counters = new Map();
|
|
45
|
+
const items = [];
|
|
46
|
+
const refs = [];
|
|
47
|
+
for (const candidate of rawCandidates) {
|
|
48
|
+
if (options.interactive && !INTERACTIVE_KINDS.has(candidate.kind)) {
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
const text = candidateText(candidate);
|
|
52
|
+
if (!text) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const key = buildCandidateKey(candidate);
|
|
56
|
+
const nth = counters.get(key) ?? 0;
|
|
57
|
+
counters.set(key, nth + 1);
|
|
58
|
+
const ref = `@e${items.length + 1}`;
|
|
59
|
+
items.push({
|
|
60
|
+
ref,
|
|
61
|
+
kind: candidate.kind,
|
|
62
|
+
text,
|
|
63
|
+
role: candidate.role,
|
|
64
|
+
flags: [
|
|
65
|
+
...(candidate.checked === true ? ["checked"] : []),
|
|
66
|
+
...(candidate.disabled === true ? ["disabled"] : []),
|
|
67
|
+
...(normalizeWhitespace(candidate.selectedText) ? [`selected=${normalizeWhitespace(candidate.selectedText)}`] : [])
|
|
68
|
+
]
|
|
69
|
+
});
|
|
70
|
+
refs.push({
|
|
71
|
+
ref,
|
|
72
|
+
kind: candidate.kind,
|
|
73
|
+
tag: candidate.tag,
|
|
74
|
+
role: candidate.role,
|
|
75
|
+
text: normalizeWhitespace(candidate.text),
|
|
76
|
+
accessibleName: normalizeWhitespace(candidate.accessibleName),
|
|
77
|
+
ariaLabel: normalizeWhitespace(candidate.ariaLabel),
|
|
78
|
+
placeholder: normalizeWhitespace(candidate.placeholder),
|
|
79
|
+
inputType: normalizeWhitespace(candidate.inputType),
|
|
80
|
+
name: normalizeWhitespace(candidate.name),
|
|
81
|
+
href: normalizeWhitespace(candidate.href),
|
|
82
|
+
nth,
|
|
83
|
+
checked: candidate.checked,
|
|
84
|
+
disabled: candidate.disabled,
|
|
85
|
+
selectedText: normalizeWhitespace(candidate.selectedText)
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return { items, refs };
|
|
89
|
+
}
|
|
90
|
+
async function buildSnapshot(page, options = {}) {
|
|
91
|
+
const candidates = await page.evaluate(() => {
|
|
92
|
+
const selectors = [
|
|
93
|
+
"h1",
|
|
94
|
+
"h2",
|
|
95
|
+
"h3",
|
|
96
|
+
"h4",
|
|
97
|
+
"h5",
|
|
98
|
+
"h6",
|
|
99
|
+
"a",
|
|
100
|
+
"button",
|
|
101
|
+
"input",
|
|
102
|
+
"textarea",
|
|
103
|
+
"select",
|
|
104
|
+
"p",
|
|
105
|
+
"[role='heading']",
|
|
106
|
+
"[role='link']",
|
|
107
|
+
"[role='button']",
|
|
108
|
+
"[role='textbox']"
|
|
109
|
+
].join(",");
|
|
110
|
+
const seen = new WeakSet();
|
|
111
|
+
function normalize(value) {
|
|
112
|
+
if (!value) {
|
|
113
|
+
return undefined;
|
|
114
|
+
}
|
|
115
|
+
const trimmed = value.replace(/\s+/g, " ").trim();
|
|
116
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
117
|
+
}
|
|
118
|
+
function isVisible(element) {
|
|
119
|
+
const htmlElement = element;
|
|
120
|
+
const style = window.getComputedStyle(htmlElement);
|
|
121
|
+
const rect = htmlElement.getBoundingClientRect();
|
|
122
|
+
return (style.display !== "none" &&
|
|
123
|
+
style.visibility !== "hidden" &&
|
|
124
|
+
style.opacity !== "0" &&
|
|
125
|
+
rect.width > 0 &&
|
|
126
|
+
rect.height > 0);
|
|
127
|
+
}
|
|
128
|
+
function implicitRole(element) {
|
|
129
|
+
const tag = element.tagName.toLowerCase();
|
|
130
|
+
if (/^h[1-6]$/.test(tag)) {
|
|
131
|
+
return "heading";
|
|
132
|
+
}
|
|
133
|
+
if (tag === "a") {
|
|
134
|
+
return "link";
|
|
135
|
+
}
|
|
136
|
+
if (tag === "button") {
|
|
137
|
+
return "button";
|
|
138
|
+
}
|
|
139
|
+
if (tag === "textarea") {
|
|
140
|
+
return "textbox";
|
|
141
|
+
}
|
|
142
|
+
if (tag === "select") {
|
|
143
|
+
return "combobox";
|
|
144
|
+
}
|
|
145
|
+
if (tag === "input") {
|
|
146
|
+
const type = element.type.toLowerCase();
|
|
147
|
+
if (type === "submit" || type === "button" || type === "reset") {
|
|
148
|
+
return "button";
|
|
149
|
+
}
|
|
150
|
+
if (type === "checkbox") {
|
|
151
|
+
return "checkbox";
|
|
152
|
+
}
|
|
153
|
+
if (type === "radio") {
|
|
154
|
+
return "radio";
|
|
155
|
+
}
|
|
156
|
+
return "textbox";
|
|
157
|
+
}
|
|
158
|
+
return undefined;
|
|
159
|
+
}
|
|
160
|
+
function associatedLabel(element) {
|
|
161
|
+
if (element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement || element instanceof HTMLSelectElement) {
|
|
162
|
+
const labels = Array.from(element.labels ?? []);
|
|
163
|
+
const text = labels
|
|
164
|
+
.map((label) => normalize(label.textContent))
|
|
165
|
+
.filter((value) => Boolean(value))
|
|
166
|
+
.join(" ");
|
|
167
|
+
return normalize(text);
|
|
168
|
+
}
|
|
169
|
+
return undefined;
|
|
170
|
+
}
|
|
171
|
+
function kindFor(element) {
|
|
172
|
+
const tag = element.tagName.toLowerCase();
|
|
173
|
+
const role = element.getAttribute("role");
|
|
174
|
+
if (/^h[1-6]$/.test(tag) || role === "heading") {
|
|
175
|
+
return "heading";
|
|
176
|
+
}
|
|
177
|
+
if (tag === "a" || role === "link") {
|
|
178
|
+
return "link";
|
|
179
|
+
}
|
|
180
|
+
if (tag === "button" || role === "button") {
|
|
181
|
+
return "button";
|
|
182
|
+
}
|
|
183
|
+
if (tag === "textarea") {
|
|
184
|
+
return "textarea";
|
|
185
|
+
}
|
|
186
|
+
if (tag === "select") {
|
|
187
|
+
return "select";
|
|
188
|
+
}
|
|
189
|
+
if (tag === "input") {
|
|
190
|
+
const inputType = element.type.toLowerCase();
|
|
191
|
+
if (inputType === "checkbox") {
|
|
192
|
+
return "checkbox";
|
|
193
|
+
}
|
|
194
|
+
if (inputType === "radio") {
|
|
195
|
+
return "radio";
|
|
196
|
+
}
|
|
197
|
+
return "input";
|
|
198
|
+
}
|
|
199
|
+
if (role === "textbox") {
|
|
200
|
+
return "input";
|
|
201
|
+
}
|
|
202
|
+
if (tag === "p") {
|
|
203
|
+
return "paragraph";
|
|
204
|
+
}
|
|
205
|
+
return undefined;
|
|
206
|
+
}
|
|
207
|
+
function isParagraphWrappedInteractive(element) {
|
|
208
|
+
if (element.tagName.toLowerCase() !== "p") {
|
|
209
|
+
return false;
|
|
210
|
+
}
|
|
211
|
+
const children = Array.from(element.children);
|
|
212
|
+
if (children.length !== 1) {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
const childTag = children[0]?.tagName.toLowerCase();
|
|
216
|
+
return childTag === "a" || childTag === "button";
|
|
217
|
+
}
|
|
218
|
+
const result = [];
|
|
219
|
+
for (const element of Array.from(document.querySelectorAll(selectors))) {
|
|
220
|
+
if (seen.has(element) || !isVisible(element)) {
|
|
221
|
+
continue;
|
|
222
|
+
}
|
|
223
|
+
seen.add(element);
|
|
224
|
+
const tag = element.tagName.toLowerCase();
|
|
225
|
+
const kind = kindFor(element);
|
|
226
|
+
if (!kind) {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (isParagraphWrappedInteractive(element)) {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
const explicitRole = normalize(element.getAttribute("role"));
|
|
233
|
+
const role = explicitRole ?? implicitRole(element);
|
|
234
|
+
const text = tag === "input" || tag === "textarea" || tag === "select"
|
|
235
|
+
? undefined
|
|
236
|
+
: normalize(element.innerText || element.textContent);
|
|
237
|
+
const placeholder = element instanceof HTMLInputElement || element instanceof HTMLTextAreaElement
|
|
238
|
+
? normalize(element.placeholder)
|
|
239
|
+
: undefined;
|
|
240
|
+
const inputType = element instanceof HTMLInputElement ? normalize(element.type) : undefined;
|
|
241
|
+
const name = element instanceof HTMLInputElement ||
|
|
242
|
+
element instanceof HTMLTextAreaElement ||
|
|
243
|
+
element instanceof HTMLSelectElement
|
|
244
|
+
? normalize(element.name)
|
|
245
|
+
: undefined;
|
|
246
|
+
const ariaLabel = normalize(element.getAttribute("aria-label"));
|
|
247
|
+
const label = associatedLabel(element);
|
|
248
|
+
const checked = element instanceof HTMLInputElement ? element.checked : undefined;
|
|
249
|
+
const disabled = element instanceof HTMLInputElement ||
|
|
250
|
+
element instanceof HTMLTextAreaElement ||
|
|
251
|
+
element instanceof HTMLSelectElement ||
|
|
252
|
+
element instanceof HTMLButtonElement
|
|
253
|
+
? element.disabled
|
|
254
|
+
: element.hasAttribute("disabled");
|
|
255
|
+
const selectedText = element instanceof HTMLSelectElement
|
|
256
|
+
? normalize(Array.from(element.selectedOptions)
|
|
257
|
+
.map((option) => option.textContent ?? "")
|
|
258
|
+
.join(" "))
|
|
259
|
+
: undefined;
|
|
260
|
+
const accessibleName = ariaLabel ??
|
|
261
|
+
label ??
|
|
262
|
+
normalize(element.innerText || element.textContent) ??
|
|
263
|
+
placeholder ??
|
|
264
|
+
name;
|
|
265
|
+
const href = element instanceof HTMLAnchorElement ? normalize(element.href) : undefined;
|
|
266
|
+
result.push({
|
|
267
|
+
kind,
|
|
268
|
+
tag,
|
|
269
|
+
role,
|
|
270
|
+
text,
|
|
271
|
+
accessibleName,
|
|
272
|
+
ariaLabel,
|
|
273
|
+
placeholder,
|
|
274
|
+
inputType,
|
|
275
|
+
name,
|
|
276
|
+
href,
|
|
277
|
+
checked,
|
|
278
|
+
disabled,
|
|
279
|
+
selectedText
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
return result;
|
|
283
|
+
});
|
|
284
|
+
return buildSnapshotModel(candidates, options);
|
|
285
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.loadConfig = loadConfig;
|
|
7
|
+
exports.ensureStateDir = ensureStateDir;
|
|
8
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
9
|
+
const node_os_1 = __importDefault(require("node:os"));
|
|
10
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
11
|
+
const DEFAULT_CONNECT_BASE = "https://cloudbrowser.gologin.com/connect";
|
|
12
|
+
const DEFAULT_DAEMON_PORT = 44777;
|
|
13
|
+
const DEFAULT_DAEMON_HOST = "127.0.0.1";
|
|
14
|
+
const DEFAULT_NAVIGATION_TIMEOUT_MS = 30_000;
|
|
15
|
+
const DEFAULT_ACTION_TIMEOUT_MS = 10_000;
|
|
16
|
+
function parsePort(value, fallback) {
|
|
17
|
+
if (!value) {
|
|
18
|
+
return fallback;
|
|
19
|
+
}
|
|
20
|
+
const port = Number(value);
|
|
21
|
+
return Number.isInteger(port) && port > 0 ? port : fallback;
|
|
22
|
+
}
|
|
23
|
+
function readConfigFile(configPath) {
|
|
24
|
+
if (!node_fs_1.default.existsSync(configPath)) {
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const raw = node_fs_1.default.readFileSync(configPath, "utf8");
|
|
29
|
+
const parsed = JSON.parse(raw);
|
|
30
|
+
return parsed;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function loadConfig() {
|
|
37
|
+
const homeDir = node_os_1.default.homedir();
|
|
38
|
+
const baseDir = node_path_1.default.join(homeDir, ".gologin-agent-browser");
|
|
39
|
+
const configPath = node_path_1.default.join(baseDir, "config.json");
|
|
40
|
+
const logPath = node_path_1.default.join(baseDir, "daemon.log");
|
|
41
|
+
const socketPath = node_path_1.default.join(node_os_1.default.tmpdir(), "gologin-agent-browser.sock");
|
|
42
|
+
const fileConfig = readConfigFile(configPath);
|
|
43
|
+
return {
|
|
44
|
+
token: process.env.GOLOGIN_TOKEN ?? fileConfig.token,
|
|
45
|
+
defaultProfileId: process.env.GOLOGIN_PROFILE_ID ?? fileConfig.defaultProfileId,
|
|
46
|
+
connectBase: process.env.GOLOGIN_CONNECT_BASE ?? fileConfig.connectBase ?? DEFAULT_CONNECT_BASE,
|
|
47
|
+
daemonPort: parsePort(process.env.GOLOGIN_DAEMON_PORT, fileConfig.daemonPort ?? DEFAULT_DAEMON_PORT),
|
|
48
|
+
daemonHost: DEFAULT_DAEMON_HOST,
|
|
49
|
+
socketPath,
|
|
50
|
+
configPath,
|
|
51
|
+
logPath,
|
|
52
|
+
navigationTimeoutMs: DEFAULT_NAVIGATION_TIMEOUT_MS,
|
|
53
|
+
actionTimeoutMs: DEFAULT_ACTION_TIMEOUT_MS
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
function ensureStateDir(config) {
|
|
57
|
+
node_fs_1.default.mkdirSync(node_path_1.default.dirname(config.configPath), { recursive: true });
|
|
58
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { DaemonErrorPayload } from "./types";
|
|
2
|
+
export type ErrorCode = "DAEMON_UNREACHABLE" | "TOKEN_MISSING" | "PROFILE_MISSING" | "SESSION_NOT_FOUND" | "SESSION_EXPIRED" | "REF_NOT_FOUND" | "BROWSER_CONNECTION_FAILED" | "NAVIGATION_TIMEOUT" | "SCREENSHOT_FAILED" | "PDF_FAILED" | "UPLOAD_FAILED" | "BAD_REQUEST" | "INTERNAL_ERROR";
|
|
3
|
+
export declare class AppError extends Error {
|
|
4
|
+
readonly code: ErrorCode;
|
|
5
|
+
readonly status: number;
|
|
6
|
+
readonly details?: Record<string, unknown>;
|
|
7
|
+
constructor(code: ErrorCode, message: string, status?: number, details?: Record<string, unknown>);
|
|
8
|
+
}
|
|
9
|
+
export declare function isDaemonErrorPayload(value: unknown): value is DaemonErrorPayload;
|
|
10
|
+
export declare function serializeError(error: unknown): DaemonErrorPayload;
|
|
11
|
+
export declare function fromDaemonError(payload: DaemonErrorPayload): AppError;
|
|
12
|
+
export declare function formatErrorLine(error: unknown): string;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AppError = void 0;
|
|
4
|
+
exports.isDaemonErrorPayload = isDaemonErrorPayload;
|
|
5
|
+
exports.serializeError = serializeError;
|
|
6
|
+
exports.fromDaemonError = fromDaemonError;
|
|
7
|
+
exports.formatErrorLine = formatErrorLine;
|
|
8
|
+
class AppError extends Error {
|
|
9
|
+
code;
|
|
10
|
+
status;
|
|
11
|
+
details;
|
|
12
|
+
constructor(code, message, status = 500, details) {
|
|
13
|
+
super(message);
|
|
14
|
+
this.name = "AppError";
|
|
15
|
+
this.code = code;
|
|
16
|
+
this.status = status;
|
|
17
|
+
this.details = details;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
exports.AppError = AppError;
|
|
21
|
+
function isDaemonErrorPayload(value) {
|
|
22
|
+
if (!value || typeof value !== "object") {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const candidate = value;
|
|
26
|
+
return (typeof candidate.code === "string" &&
|
|
27
|
+
typeof candidate.message === "string" &&
|
|
28
|
+
typeof candidate.status === "number");
|
|
29
|
+
}
|
|
30
|
+
function serializeError(error) {
|
|
31
|
+
if (error instanceof AppError) {
|
|
32
|
+
return {
|
|
33
|
+
code: error.code,
|
|
34
|
+
message: error.message,
|
|
35
|
+
status: error.status,
|
|
36
|
+
details: error.details
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (error instanceof Error) {
|
|
40
|
+
return {
|
|
41
|
+
code: "INTERNAL_ERROR",
|
|
42
|
+
message: error.message,
|
|
43
|
+
status: 500
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
return {
|
|
47
|
+
code: "INTERNAL_ERROR",
|
|
48
|
+
message: String(error),
|
|
49
|
+
status: 500
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function fromDaemonError(payload) {
|
|
53
|
+
return new AppError(payload.code, payload.message, payload.status, payload.details);
|
|
54
|
+
}
|
|
55
|
+
function formatErrorLine(error) {
|
|
56
|
+
if (error instanceof AppError) {
|
|
57
|
+
return `${error.code}: ${error.message}`;
|
|
58
|
+
}
|
|
59
|
+
if (error instanceof Error) {
|
|
60
|
+
return `${error.name}: ${error.message}`;
|
|
61
|
+
}
|
|
62
|
+
return `INTERNAL_ERROR: ${String(error)}`;
|
|
63
|
+
}
|