diggylab 0.1.50 → 0.1.51
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 +3 -0
- package/cli.js +171 -120
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,9 @@ The Desktop Agent also runs headless as a CLI worker, for customers who scan fro
|
|
|
4
4
|
instead of the desktop app. It pairs like any device, receives `DESKTOP` scan jobs, and scans with
|
|
5
5
|
headless Chromium — the Electron app is not involved.
|
|
6
6
|
|
|
7
|
+
For the platform-run scan worker (admin-managed Proxmox/Linux nodes with one Facebook account and an
|
|
8
|
+
optional proxy), see [worker-agent.md](worker-agent.md).
|
|
9
|
+
|
|
7
10
|
## Requirements
|
|
8
11
|
|
|
9
12
|
- Node.js 20+
|
package/cli.js
CHANGED
|
@@ -8,6 +8,93 @@ import { hostname, homedir } from "node:os";
|
|
|
8
8
|
import { dirname as dirname3, join as join4 } from "node:path";
|
|
9
9
|
import { fileURLToPath } from "node:url";
|
|
10
10
|
|
|
11
|
+
// ../../packages/shared/src/index.ts
|
|
12
|
+
var DESKTOP_AGENT_PROTOCOL_VERSION = 1;
|
|
13
|
+
var DESKTOP_POST_COMMENTS_MAX_LIMIT = 200;
|
|
14
|
+
var DESKTOP_POST_COMMENTS_REQUEST_TIMEOUT_MS = 3 * 60 * 1e3;
|
|
15
|
+
var FACEBOOK_DOMAIN = ".facebook.com";
|
|
16
|
+
var FACEBOOK_HOST = /(^|\.)facebook\.com$/i;
|
|
17
|
+
function isFacebookDomain(domain) {
|
|
18
|
+
return Boolean(domain && FACEBOOK_HOST.test(domain.replace(/^\./, "")));
|
|
19
|
+
}
|
|
20
|
+
function normalizeSameSite(value) {
|
|
21
|
+
if (typeof value !== "string") return void 0;
|
|
22
|
+
const lower = value.toLowerCase().replace(/[^a-z]/g, "");
|
|
23
|
+
if (lower === "lax") return "Lax";
|
|
24
|
+
if (lower === "strict") return "Strict";
|
|
25
|
+
if (lower === "none" || lower === "norestriction") return "None";
|
|
26
|
+
return void 0;
|
|
27
|
+
}
|
|
28
|
+
function toCookie(value) {
|
|
29
|
+
const name = typeof value.name === "string" ? value.name.trim() : "";
|
|
30
|
+
const cookieValue = typeof value.value === "string" ? value.value : "";
|
|
31
|
+
if (!name || !cookieValue) return null;
|
|
32
|
+
return {
|
|
33
|
+
domain: typeof value.domain === "string" ? value.domain : void 0,
|
|
34
|
+
expires: typeof value.expires === "number" ? value.expires : typeof value.expirationDate === "number" ? value.expirationDate : void 0,
|
|
35
|
+
httpOnly: typeof value.httpOnly === "boolean" ? value.httpOnly : void 0,
|
|
36
|
+
name,
|
|
37
|
+
path: typeof value.path === "string" ? value.path : void 0,
|
|
38
|
+
sameSite: normalizeSameSite(value.sameSite),
|
|
39
|
+
secure: typeof value.secure === "boolean" ? value.secure : void 0,
|
|
40
|
+
value: cookieValue
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function parseCookieHeader(input) {
|
|
44
|
+
const header = input.replace(/^\s*cookie\s*:/i, "");
|
|
45
|
+
return header.split(/[;\n]/).map((part) => part.trim()).filter(Boolean).map((part) => {
|
|
46
|
+
const separator = part.indexOf("=");
|
|
47
|
+
if (separator <= 0) return null;
|
|
48
|
+
return { name: part.slice(0, separator).trim(), value: part.slice(separator + 1) };
|
|
49
|
+
}).filter((cookie) => cookie !== null);
|
|
50
|
+
}
|
|
51
|
+
function parseCookieTable(input) {
|
|
52
|
+
const cookies = [];
|
|
53
|
+
for (const line of input.split("\n")) {
|
|
54
|
+
const columns = line.split(" ");
|
|
55
|
+
const name = columns[0]?.trim() ?? "";
|
|
56
|
+
const value = columns[1];
|
|
57
|
+
if (!value || !name || name === "Name") continue;
|
|
58
|
+
cookies.push({ domain: columns[2]?.trim(), name, value });
|
|
59
|
+
}
|
|
60
|
+
return cookies;
|
|
61
|
+
}
|
|
62
|
+
function parseCookieInput(input) {
|
|
63
|
+
const trimmed = input.trim();
|
|
64
|
+
if (!trimmed) return [];
|
|
65
|
+
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
|
|
66
|
+
return trimmed.includes(" ") ? parseCookieTable(trimmed) : parseCookieHeader(trimmed);
|
|
67
|
+
}
|
|
68
|
+
try {
|
|
69
|
+
const parsed = JSON.parse(trimmed);
|
|
70
|
+
if (Array.isArray(parsed)) {
|
|
71
|
+
return parsed.map((entry) => entry && typeof entry === "object" ? toCookie(entry) : null).filter((cookie) => cookie !== null);
|
|
72
|
+
}
|
|
73
|
+
if (parsed && typeof parsed === "object") {
|
|
74
|
+
const cookies = parsed.cookies;
|
|
75
|
+
if (Array.isArray(cookies)) {
|
|
76
|
+
return cookies.map((entry) => entry && typeof entry === "object" ? toCookie(entry) : null).filter((cookie) => cookie !== null);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
} catch {
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
function toFacebookStorageState(cookies) {
|
|
85
|
+
const usable = cookies.filter((cookie) => isFacebookDomain(cookie.domain) || cookie.domain === void 0).map((cookie) => ({
|
|
86
|
+
domain: isFacebookDomain(cookie.domain) ? cookie.domain : FACEBOOK_DOMAIN,
|
|
87
|
+
expires: typeof cookie.expires === "number" && cookie.expires > 0 ? cookie.expires : -1,
|
|
88
|
+
httpOnly: cookie.httpOnly ?? true,
|
|
89
|
+
name: cookie.name,
|
|
90
|
+
path: cookie.path?.startsWith("/") ? cookie.path : "/",
|
|
91
|
+
sameSite: cookie.sameSite ?? "Lax",
|
|
92
|
+
secure: cookie.secure ?? true,
|
|
93
|
+
value: cookie.value
|
|
94
|
+
}));
|
|
95
|
+
return { cookies: usable, origins: [] };
|
|
96
|
+
}
|
|
97
|
+
|
|
11
98
|
// ../../packages/facebook-scanner/dist/parser.js
|
|
12
99
|
var COMMENT_TEXT_LIMIT = 2e3;
|
|
13
100
|
var COMMENT_AUTHOR_LIMIT = 200;
|
|
@@ -708,6 +795,7 @@ var FacebookScannerError = class extends Error {
|
|
|
708
795
|
};
|
|
709
796
|
var FacebookScanner = class {
|
|
710
797
|
browserType;
|
|
798
|
+
proxy;
|
|
711
799
|
extractDomItems;
|
|
712
800
|
extractDomComments;
|
|
713
801
|
parseGraphqlBody;
|
|
@@ -717,6 +805,7 @@ var FacebookScanner = class {
|
|
|
717
805
|
running = false;
|
|
718
806
|
constructor(options) {
|
|
719
807
|
this.browserType = options.browserType;
|
|
808
|
+
this.proxy = options.proxy;
|
|
720
809
|
this.extractDomItems = options.extractDomItems ?? extractFacebookDomItems;
|
|
721
810
|
this.extractDomComments = options.extractDomComments ?? extractFacebookDomComments;
|
|
722
811
|
this.parseGraphqlBody = options.parseGraphqlBody ?? parseFacebookGraphqlBody;
|
|
@@ -730,7 +819,10 @@ var FacebookScanner = class {
|
|
|
730
819
|
this.running = true;
|
|
731
820
|
let browser = null;
|
|
732
821
|
try {
|
|
733
|
-
browser = await this.browserType.launch({
|
|
822
|
+
browser = await this.browserType.launch({
|
|
823
|
+
headless: true,
|
|
824
|
+
...this.proxy ? { proxy: this.proxy } : {}
|
|
825
|
+
});
|
|
734
826
|
const results = [];
|
|
735
827
|
for (const [index, groupUrl] of options.groupUrls.entries()) {
|
|
736
828
|
assertNotAborted(options.signal);
|
|
@@ -753,7 +845,10 @@ var FacebookScanner = class {
|
|
|
753
845
|
this.running = true;
|
|
754
846
|
let browser = null;
|
|
755
847
|
try {
|
|
756
|
-
browser = await this.browserType.launch({
|
|
848
|
+
browser = await this.browserType.launch({
|
|
849
|
+
headless: true,
|
|
850
|
+
...this.proxy ? { proxy: this.proxy } : {}
|
|
851
|
+
});
|
|
757
852
|
const context = await browser.newContext({ storageState: options.storageState });
|
|
758
853
|
let page = null;
|
|
759
854
|
const graphqlComments = [];
|
|
@@ -1360,13 +1455,6 @@ function isDeviceSummary(value) {
|
|
|
1360
1455
|
|
|
1361
1456
|
// src/main/desktop-agent-session.ts
|
|
1362
1457
|
import { randomUUID } from "node:crypto";
|
|
1363
|
-
|
|
1364
|
-
// ../../packages/shared/src/index.ts
|
|
1365
|
-
var DESKTOP_AGENT_PROTOCOL_VERSION = 1;
|
|
1366
|
-
var DESKTOP_POST_COMMENTS_MAX_LIMIT = 200;
|
|
1367
|
-
var DESKTOP_POST_COMMENTS_REQUEST_TIMEOUT_MS = 3 * 60 * 1e3;
|
|
1368
|
-
|
|
1369
|
-
// src/main/desktop-agent-session.ts
|
|
1370
1458
|
var MESSAGE_FRESHNESS_MS = 5 * 60 * 1e3;
|
|
1371
1459
|
var MAX_REPLAY_IDS = 512;
|
|
1372
1460
|
var DesktopAgentSession = class {
|
|
@@ -1527,6 +1615,7 @@ var DesktopAgentSession = class {
|
|
|
1527
1615
|
envelope.payload.challenge
|
|
1528
1616
|
),
|
|
1529
1617
|
agentVersion: this.options.agentVersion,
|
|
1618
|
+
...this.options.clientType ? { clientType: this.options.clientType } : {},
|
|
1530
1619
|
platform: this.options.platform,
|
|
1531
1620
|
state: this.reportedState,
|
|
1532
1621
|
facebookReady: this.options.getFacebookReady(),
|
|
@@ -1594,10 +1683,12 @@ var DesktopAgentSession = class {
|
|
|
1594
1683
|
this.clearHeartbeat();
|
|
1595
1684
|
this.heartbeatTimer = this.options.setTimer(() => {
|
|
1596
1685
|
if (generation !== this.generation || socket !== this.socket) return;
|
|
1686
|
+
const metrics = this.options.metrics?.();
|
|
1597
1687
|
this.send(socket, deviceId, "HEARTBEAT", {
|
|
1598
1688
|
state: this.reportedState,
|
|
1599
1689
|
facebookReady: this.options.getFacebookReady(),
|
|
1600
|
-
currentJobId: this.reportedJobId
|
|
1690
|
+
currentJobId: this.reportedJobId,
|
|
1691
|
+
...metrics ? { metrics } : {}
|
|
1601
1692
|
});
|
|
1602
1693
|
this.scheduleHeartbeat(socket, deviceId, interval, generation);
|
|
1603
1694
|
}, interval);
|
|
@@ -1703,9 +1794,9 @@ var DesktopAgentSession = class {
|
|
|
1703
1794
|
if (this.accessRefreshTimer) this.options.clearTimer(this.accessRefreshTimer);
|
|
1704
1795
|
this.accessRefreshTimer = null;
|
|
1705
1796
|
}
|
|
1706
|
-
update(
|
|
1707
|
-
this.snapshot =
|
|
1708
|
-
for (const listener of this.listeners) listener(
|
|
1797
|
+
update(snapshot2) {
|
|
1798
|
+
this.snapshot = snapshot2;
|
|
1799
|
+
for (const listener of this.listeners) listener(snapshot2);
|
|
1709
1800
|
}
|
|
1710
1801
|
};
|
|
1711
1802
|
function parseServerEnvelope(data) {
|
|
@@ -2542,9 +2633,9 @@ var DesktopScanRunner = class {
|
|
|
2542
2633
|
leaseExpiresAt(job) {
|
|
2543
2634
|
return this.activeAttempt?.jobId === job.jobId && this.activeAttempt.attemptToken === job.attemptToken ? this.activeAttempt.leaseExpiresAt : job.leaseExpiresAt;
|
|
2544
2635
|
}
|
|
2545
|
-
update(
|
|
2546
|
-
this.snapshot =
|
|
2547
|
-
for (const listener of this.listeners) listener(
|
|
2636
|
+
update(snapshot2) {
|
|
2637
|
+
this.snapshot = snapshot2;
|
|
2638
|
+
for (const listener of this.listeners) listener(snapshot2);
|
|
2548
2639
|
}
|
|
2549
2640
|
};
|
|
2550
2641
|
function toDesktopPost(item) {
|
|
@@ -2870,7 +2961,7 @@ var FacebookLoginService = class {
|
|
|
2870
2961
|
await this.finish({ state: "READY", errorCode: null, accountId });
|
|
2871
2962
|
return true;
|
|
2872
2963
|
}
|
|
2873
|
-
async finish(
|
|
2964
|
+
async finish(snapshot2) {
|
|
2874
2965
|
if (this.timer) this.options.clearTimer(this.timer);
|
|
2875
2966
|
this.timer = null;
|
|
2876
2967
|
const page = this.page;
|
|
@@ -2882,12 +2973,12 @@ var FacebookLoginService = class {
|
|
|
2882
2973
|
page?.off("framenavigated", this.navigationHandler);
|
|
2883
2974
|
await context?.close();
|
|
2884
2975
|
await browser?.close();
|
|
2885
|
-
this.update(
|
|
2976
|
+
this.update(snapshot2);
|
|
2886
2977
|
}
|
|
2887
|
-
update(
|
|
2888
|
-
if (
|
|
2889
|
-
this.snapshot =
|
|
2890
|
-
for (const listener of this.listeners) listener(
|
|
2978
|
+
update(snapshot2) {
|
|
2979
|
+
if (snapshot2.state === this.snapshot.state && snapshot2.errorCode === this.snapshot.errorCode && snapshot2.accountId === this.snapshot.accountId) return;
|
|
2980
|
+
this.snapshot = snapshot2;
|
|
2981
|
+
for (const listener of this.listeners) listener(snapshot2);
|
|
2891
2982
|
}
|
|
2892
2983
|
serialize(operation) {
|
|
2893
2984
|
const result = this.pending.catch(() => void 0).then(operation);
|
|
@@ -2943,6 +3034,41 @@ function getFacebookAccountId(state) {
|
|
|
2943
3034
|
return value && /^\d{1,32}$/.test(value) ? value : null;
|
|
2944
3035
|
}
|
|
2945
3036
|
|
|
3037
|
+
// src/shared/resource-metrics.ts
|
|
3038
|
+
import os from "node:os";
|
|
3039
|
+
function snapshot(cpus) {
|
|
3040
|
+
let idle = 0;
|
|
3041
|
+
let total = 0;
|
|
3042
|
+
for (const cpu of cpus) {
|
|
3043
|
+
const times = cpu.times;
|
|
3044
|
+
idle += times.idle;
|
|
3045
|
+
total += times.user + times.nice + times.sys + times.idle + times.irq;
|
|
3046
|
+
}
|
|
3047
|
+
return { idle, total };
|
|
3048
|
+
}
|
|
3049
|
+
function usageBetween(previous, current) {
|
|
3050
|
+
const totalDelta = current.total - previous.total;
|
|
3051
|
+
const idleDelta = current.idle - previous.idle;
|
|
3052
|
+
if (totalDelta <= 0) return 0;
|
|
3053
|
+
return Math.min(100, Math.max(0, (1 - idleDelta / totalDelta) * 100));
|
|
3054
|
+
}
|
|
3055
|
+
function createMetricsCollector() {
|
|
3056
|
+
let previous = null;
|
|
3057
|
+
return () => {
|
|
3058
|
+
const cpus = os.cpus();
|
|
3059
|
+
const current = snapshot(cpus);
|
|
3060
|
+
const cpuPercent = previous ? usageBetween(previous, current) : usageBetween({ idle: 0, total: 0 }, current);
|
|
3061
|
+
previous = current;
|
|
3062
|
+
const memTotalBytes = os.totalmem();
|
|
3063
|
+
return {
|
|
3064
|
+
cpuCount: cpus.length,
|
|
3065
|
+
cpuPercent: Math.round(cpuPercent * 10) / 10,
|
|
3066
|
+
memTotalBytes,
|
|
3067
|
+
memUsedBytes: Math.max(0, memTotalBytes - os.freemem())
|
|
3068
|
+
};
|
|
3069
|
+
};
|
|
3070
|
+
}
|
|
3071
|
+
|
|
2946
3072
|
// src/main/graphql-dump.ts
|
|
2947
3073
|
import { writeFile as writeFile4 } from "node:fs/promises";
|
|
2948
3074
|
import { join as join3 } from "node:path";
|
|
@@ -3047,90 +3173,6 @@ function browserInstallCommands(platform) {
|
|
|
3047
3173
|
return platform === "linux" ? [["install", "--with-deps", "chromium"], install] : [install];
|
|
3048
3174
|
}
|
|
3049
3175
|
|
|
3050
|
-
// src/cli/cookie-import.ts
|
|
3051
|
-
var FACEBOOK_DOMAIN = ".facebook.com";
|
|
3052
|
-
var FACEBOOK_HOST = /(^|\.)facebook\.com$/i;
|
|
3053
|
-
function isFacebookDomain(domain) {
|
|
3054
|
-
return Boolean(domain && FACEBOOK_HOST.test(domain.replace(/^\./, "")));
|
|
3055
|
-
}
|
|
3056
|
-
function normalizeSameSite(value) {
|
|
3057
|
-
if (typeof value !== "string") return void 0;
|
|
3058
|
-
const lower = value.toLowerCase().replace(/[^a-z]/g, "");
|
|
3059
|
-
if (lower === "lax") return "Lax";
|
|
3060
|
-
if (lower === "strict") return "Strict";
|
|
3061
|
-
if (lower === "none" || lower === "norestriction") return "None";
|
|
3062
|
-
return void 0;
|
|
3063
|
-
}
|
|
3064
|
-
function toCookie(value) {
|
|
3065
|
-
const name = typeof value.name === "string" ? value.name.trim() : "";
|
|
3066
|
-
const cookieValue = typeof value.value === "string" ? value.value : "";
|
|
3067
|
-
if (!name || !cookieValue) return null;
|
|
3068
|
-
return {
|
|
3069
|
-
domain: typeof value.domain === "string" ? value.domain : void 0,
|
|
3070
|
-
expires: typeof value.expires === "number" ? value.expires : typeof value.expirationDate === "number" ? value.expirationDate : void 0,
|
|
3071
|
-
httpOnly: typeof value.httpOnly === "boolean" ? value.httpOnly : void 0,
|
|
3072
|
-
name,
|
|
3073
|
-
path: typeof value.path === "string" ? value.path : void 0,
|
|
3074
|
-
sameSite: normalizeSameSite(value.sameSite),
|
|
3075
|
-
secure: typeof value.secure === "boolean" ? value.secure : void 0,
|
|
3076
|
-
value: cookieValue
|
|
3077
|
-
};
|
|
3078
|
-
}
|
|
3079
|
-
function parseCookieHeader(input) {
|
|
3080
|
-
const header = input.replace(/^\s*cookie\s*:/i, "");
|
|
3081
|
-
return header.split(/[;\n]/).map((part) => part.trim()).filter(Boolean).map((part) => {
|
|
3082
|
-
const separator = part.indexOf("=");
|
|
3083
|
-
if (separator <= 0) return null;
|
|
3084
|
-
return { name: part.slice(0, separator).trim(), value: part.slice(separator + 1) };
|
|
3085
|
-
}).filter((cookie) => cookie !== null);
|
|
3086
|
-
}
|
|
3087
|
-
function parseCookieTable(input) {
|
|
3088
|
-
const cookies = [];
|
|
3089
|
-
for (const line of input.split("\n")) {
|
|
3090
|
-
const columns = line.split(" ");
|
|
3091
|
-
const name = columns[0]?.trim() ?? "";
|
|
3092
|
-
const value = columns[1];
|
|
3093
|
-
if (!value || !name || name === "Name") continue;
|
|
3094
|
-
cookies.push({ domain: columns[2]?.trim(), name, value });
|
|
3095
|
-
}
|
|
3096
|
-
return cookies;
|
|
3097
|
-
}
|
|
3098
|
-
function parseCookieInput(input) {
|
|
3099
|
-
const trimmed = input.trim();
|
|
3100
|
-
if (!trimmed) return [];
|
|
3101
|
-
if (!trimmed.startsWith("{") && !trimmed.startsWith("[")) {
|
|
3102
|
-
return trimmed.includes(" ") ? parseCookieTable(trimmed) : parseCookieHeader(trimmed);
|
|
3103
|
-
}
|
|
3104
|
-
try {
|
|
3105
|
-
const parsed = JSON.parse(trimmed);
|
|
3106
|
-
if (Array.isArray(parsed)) {
|
|
3107
|
-
return parsed.map((entry) => entry && typeof entry === "object" ? toCookie(entry) : null).filter((cookie) => cookie !== null);
|
|
3108
|
-
}
|
|
3109
|
-
if (parsed && typeof parsed === "object") {
|
|
3110
|
-
const cookies = parsed.cookies;
|
|
3111
|
-
if (Array.isArray(cookies)) {
|
|
3112
|
-
return cookies.map((entry) => entry && typeof entry === "object" ? toCookie(entry) : null).filter((cookie) => cookie !== null);
|
|
3113
|
-
}
|
|
3114
|
-
}
|
|
3115
|
-
} catch {
|
|
3116
|
-
return [];
|
|
3117
|
-
}
|
|
3118
|
-
return [];
|
|
3119
|
-
}
|
|
3120
|
-
function toFacebookStorageState(cookies) {
|
|
3121
|
-
const usable = cookies.filter((cookie) => isFacebookDomain(cookie.domain) || cookie.domain === void 0).map((cookie) => ({
|
|
3122
|
-
domain: isFacebookDomain(cookie.domain) ? cookie.domain : FACEBOOK_DOMAIN,
|
|
3123
|
-
expires: typeof cookie.expires === "number" && cookie.expires > 0 ? cookie.expires : -1,
|
|
3124
|
-
httpOnly: cookie.httpOnly ?? true,
|
|
3125
|
-
name: cookie.name,
|
|
3126
|
-
path: cookie.path?.startsWith("/") ? cookie.path : "/",
|
|
3127
|
-
sameSite: cookie.sameSite ?? "Lax",
|
|
3128
|
-
secure: cookie.secure ?? true,
|
|
3129
|
-
value: cookie.value
|
|
3130
|
-
}));
|
|
3131
|
-
return { cookies: usable, origins: [] };
|
|
3132
|
-
}
|
|
3133
|
-
|
|
3134
3176
|
// src/cli/file-crypto.ts
|
|
3135
3177
|
import { createCipheriv, createDecipheriv, randomBytes } from "node:crypto";
|
|
3136
3178
|
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
@@ -3184,15 +3226,15 @@ var SERVICE_PATH = `/etc/systemd/system/${SERVICE_NAME}.service`;
|
|
|
3184
3226
|
function quote(value) {
|
|
3185
3227
|
return /\s/.test(value) ? `"${value.replace(/"/g, '\\"')}"` : value;
|
|
3186
3228
|
}
|
|
3187
|
-
function
|
|
3229
|
+
function buildServiceUnit(options) {
|
|
3188
3230
|
return `[Unit]
|
|
3189
|
-
Description
|
|
3231
|
+
Description=${options.description}
|
|
3190
3232
|
After=network-online.target
|
|
3191
3233
|
Wants=network-online.target
|
|
3192
3234
|
|
|
3193
3235
|
[Service]
|
|
3194
3236
|
ExecStart=${options.execStart.map(quote).join(" ")} run
|
|
3195
|
-
Environment="
|
|
3237
|
+
Environment="${options.environmentName}=${options.configDirectory}"
|
|
3196
3238
|
Environment="DIGGYLAB_API_URL=${options.apiOrigin}"
|
|
3197
3239
|
Restart=on-failure
|
|
3198
3240
|
RestartSec=10
|
|
@@ -3201,6 +3243,13 @@ RestartSec=10
|
|
|
3201
3243
|
WantedBy=multi-user.target
|
|
3202
3244
|
`;
|
|
3203
3245
|
}
|
|
3246
|
+
function buildSystemdUnit(options) {
|
|
3247
|
+
return buildServiceUnit({
|
|
3248
|
+
...options,
|
|
3249
|
+
description: "DiggyLab Desktop Agent",
|
|
3250
|
+
environmentName: "DIGGYLAB_AGENT_DIR"
|
|
3251
|
+
});
|
|
3252
|
+
}
|
|
3204
3253
|
|
|
3205
3254
|
// src/cli/cli.ts
|
|
3206
3255
|
var LOGIN_TIMEOUT_MS = 10 * 60 * 1e3;
|
|
@@ -3289,17 +3338,17 @@ function waitForFacebookLogin(login, timeoutMs) {
|
|
|
3289
3338
|
reject(new Error("Timed out waiting for the Facebook login."));
|
|
3290
3339
|
}, timeoutMs);
|
|
3291
3340
|
timer.unref();
|
|
3292
|
-
const unsubscribe = login.subscribe((
|
|
3293
|
-
if (
|
|
3341
|
+
const unsubscribe = login.subscribe((snapshot2) => {
|
|
3342
|
+
if (snapshot2.state === "READY") {
|
|
3294
3343
|
clearTimeout(timer);
|
|
3295
3344
|
unsubscribe();
|
|
3296
3345
|
resolve();
|
|
3297
3346
|
return;
|
|
3298
3347
|
}
|
|
3299
|
-
if (
|
|
3348
|
+
if (snapshot2.state === "NOT_READY" && snapshot2.errorCode) {
|
|
3300
3349
|
clearTimeout(timer);
|
|
3301
3350
|
unsubscribe();
|
|
3302
|
-
reject(new Error(`Facebook login failed (${
|
|
3351
|
+
reject(new Error(`Facebook login failed (${snapshot2.errorCode}).`));
|
|
3303
3352
|
}
|
|
3304
3353
|
});
|
|
3305
3354
|
void login.open().catch((error) => {
|
|
@@ -3609,6 +3658,8 @@ async function commandRun(runtime, io) {
|
|
|
3609
3658
|
const facebookReady = await runtime.facebook.isReady();
|
|
3610
3659
|
const session = new DesktopAgentSession({
|
|
3611
3660
|
agentVersion: agentVersion(),
|
|
3661
|
+
clientType: "CLI",
|
|
3662
|
+
metrics: createMetricsCollector(),
|
|
3612
3663
|
clearTimer: (timer) => clearTimeout(timer),
|
|
3613
3664
|
createSocket: (url, accessToken) => {
|
|
3614
3665
|
const socket = new WebSocket(url, {
|
|
@@ -3685,18 +3736,18 @@ async function commandRun(runtime, io) {
|
|
|
3685
3736
|
const replaced = new Promise((resolve) => {
|
|
3686
3737
|
onReplaced = resolve;
|
|
3687
3738
|
});
|
|
3688
|
-
session.subscribe((
|
|
3689
|
-
io.log(`${stamp()} session: ${
|
|
3690
|
-
recordActivity(`Session state changed to ${
|
|
3691
|
-
if (
|
|
3739
|
+
session.subscribe((snapshot2) => {
|
|
3740
|
+
io.log(`${stamp()} session: ${snapshot2.state}${snapshot2.errorCode ? ` (${snapshot2.errorCode})` : ""}`);
|
|
3741
|
+
recordActivity(`Session state changed to ${snapshot2.state}.`, snapshot2.state);
|
|
3742
|
+
if (snapshot2.errorCode === "CONNECTION_REPLACED") {
|
|
3692
3743
|
io.error("Another worker is already running with this device, so this one stopped.");
|
|
3693
3744
|
io.error("Stop the other worker (for example: systemctl stop diggylab), then start this one again.");
|
|
3694
3745
|
onReplaced();
|
|
3695
3746
|
}
|
|
3696
3747
|
});
|
|
3697
|
-
scanRunner.subscribe((
|
|
3698
|
-
io.log(`scanner: ${
|
|
3699
|
-
recordActivity(`Scanner state changed to ${
|
|
3748
|
+
scanRunner.subscribe((snapshot2) => {
|
|
3749
|
+
io.log(`scanner: ${snapshot2.state}${snapshot2.jobId ? ` (job ${snapshot2.jobId})` : ""}`);
|
|
3750
|
+
recordActivity(`Scanner state changed to ${snapshot2.state}.`, snapshot2.state);
|
|
3700
3751
|
});
|
|
3701
3752
|
scanRunner.start();
|
|
3702
3753
|
commentRunner.start();
|