@web-auto/webauto 0.1.3 → 0.1.6
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/apps/desktop-console/default-settings.json +2 -2
- package/apps/desktop-console/dist/main/index.mjs +915 -85
- package/apps/desktop-console/dist/main/preload.mjs +7 -0
- package/apps/desktop-console/dist/renderer/index.html +622 -50
- package/apps/desktop-console/dist/renderer/index.js +2415 -470
- package/apps/desktop-console/dist/renderer/run.mts +6 -5
- package/apps/desktop-console/entry/ui-cli.mjs +672 -0
- package/apps/desktop-console/entry/ui-console.mjs +416 -29
- package/apps/webauto/entry/account.mjs +89 -53
- package/apps/webauto/entry/browser-status.mjs +7 -10
- package/apps/webauto/entry/lib/account-detect.mjs +254 -28
- package/apps/webauto/entry/lib/account-store.mjs +219 -30
- package/apps/webauto/entry/lib/bus-publish.mjs +63 -0
- package/apps/webauto/entry/lib/camo-cli.mjs +93 -0
- package/apps/webauto/entry/lib/profilepool.mjs +14 -5
- package/apps/webauto/entry/lib/quota-status.mjs +23 -0
- package/apps/webauto/entry/lib/schedule-store.mjs +1068 -0
- package/apps/webauto/entry/profilepool.mjs +106 -17
- package/apps/webauto/entry/schedule.mjs +612 -0
- package/apps/webauto/entry/weibo-unified.mjs +134 -0
- package/apps/webauto/entry/xhs-install.mjs +236 -29
- package/apps/webauto/entry/xhs-status.mjs +5 -2
- package/apps/webauto/entry/xhs-unified.mjs +631 -98
- package/apps/webauto/resources/container-library/weibo/weibo_detail_page/comment_item/container.json +40 -0
- package/apps/webauto/resources/container-library/weibo/weibo_detail_page/reply_expand_button/container.json +38 -0
- package/apps/webauto/resources/container-library/weibo/weibo_detail_page/reply_list/container.json +37 -0
- package/apps/webauto/resources/container-library/weibo/weibo_search_page/container.json +8 -3
- package/apps/webauto/resources/container-library/weibo/weibo_search_page/login_anchor/container.json +30 -0
- package/apps/webauto/resources/container-library/weibo/weibo_search_page/search_bar/container.json +47 -0
- package/apps/webauto/resources/container-library/weibo/weibo_search_page/search_button/container.json +39 -0
- package/bin/camoufox-cli.mjs +61 -0
- package/bin/webauto.mjs +301 -54
- package/dist/modules/camo-backend/src/index.js +49 -1
- package/dist/modules/camo-backend/src/internal/BrowserSession.js +572 -3
- package/dist/modules/camo-backend/src/internal/SessionManager.js +13 -1
- package/dist/modules/camo-backend/src/internal/storage-paths.js +6 -0
- package/dist/modules/collection-manager/bloom-filter.js +91 -0
- package/dist/modules/collection-manager/date-utils.js +275 -0
- package/dist/modules/collection-manager/index.js +258 -0
- package/dist/modules/collection-manager/storage.js +195 -0
- package/dist/modules/collection-manager/types.js +47 -0
- package/dist/modules/logging/src/index.js +1 -1
- package/dist/modules/process-registry/index.js +230 -0
- package/dist/modules/rate-limiter/index.js +242 -0
- package/dist/modules/workflow/blocks/ExecuteWeiboSearchBlock.js +128 -0
- package/dist/modules/workflow/blocks/PersistXhsNoteBlock.js +7 -3
- package/dist/modules/workflow/blocks/RenderMarkdown.js +4 -1
- package/dist/modules/workflow/blocks/WeiboCollectCommentsBlock.js +282 -0
- package/dist/modules/workflow/blocks/WeiboCollectFromLinksBlock.js +283 -0
- package/dist/modules/workflow/blocks/WeiboCollectSearchLinksBlock.js +208 -0
- package/dist/modules/workflow/blocks/WeiboCollectTimelineListBlock.js +128 -0
- package/dist/modules/workflow/blocks/WeiboCollectUserPostsListBlock.js +127 -0
- package/dist/modules/workflow/blocks/helpers/downloadPaths.js +21 -0
- package/dist/modules/workflow/config/workflowRegistry.js +2 -0
- package/dist/modules/workflow/definitions/weibo-search-workflow-v1.js +47 -0
- package/dist/modules/workflow/src/runner.js +6 -0
- package/dist/modules/xiaohongshu/app/src/blocks/Phase34PersistDetailBlock.js +4 -0
- package/dist/modules/xiaohongshu/app/src/blocks/Phase3InteractBlock.js +2 -2
- package/dist/modules/xiaohongshu/app/src/blocks/helpers/sharding.js +123 -0
- package/dist/modules/xiaohongshu/app/src/container-registry/src/index.d.ts +37 -0
- package/dist/modules/xiaohongshu/app/src/container-registry/src/index.js +184 -0
- package/dist/modules/xiaohongshu/app/src/workflow/blocks/AnchorVerificationBlock.d.ts +31 -0
- package/dist/modules/xiaohongshu/app/src/workflow/blocks/AnchorVerificationBlock.js +71 -0
- package/dist/modules/xiaohongshu/app/src/workflow/blocks/DetectPageStateBlock.d.ts +48 -0
- package/dist/modules/xiaohongshu/app/src/workflow/blocks/DetectPageStateBlock.js +259 -0
- package/dist/modules/xiaohongshu/app/src/workflow/blocks/ErrorRecoveryBlock.d.ts +28 -0
- package/dist/modules/xiaohongshu/app/src/workflow/blocks/ErrorRecoveryBlock.js +319 -0
- package/dist/modules/xiaohongshu/app/src/workflow/blocks/WaitSearchPermitBlock.d.ts +36 -0
- package/dist/modules/xiaohongshu/app/src/workflow/blocks/WaitSearchPermitBlock.js +162 -0
- package/dist/modules/xiaohongshu/app/src/workflow/blocks/helpers/containerAnchors.d.ts +36 -0
- package/dist/modules/xiaohongshu/app/src/workflow/blocks/helpers/containerAnchors.js +301 -0
- package/dist/modules/xiaohongshu/app/src/workflow/blocks/helpers/operationLogger.d.ts +29 -0
- package/dist/modules/xiaohongshu/app/src/workflow/blocks/helpers/operationLogger.js +195 -0
- package/dist/modules/xiaohongshu/app/src/workflow/blocks/helpers/searchPageState.d.ts +25 -0
- package/dist/modules/xiaohongshu/app/src/workflow/blocks/helpers/searchPageState.js +164 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/MatchCommentsBlock.d.ts +66 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/MatchCommentsBlock.js +139 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase1EnsureServicesBlock.d.ts +16 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase1EnsureServicesBlock.js +36 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase1MonitorCookieBlock.d.ts +27 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase1MonitorCookieBlock.js +213 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase1StartProfileBlock.d.ts +18 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase1StartProfileBlock.js +121 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase2CollectLinksBlock.d.ts +34 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase2CollectLinksBlock.js +1249 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase2SearchBlock.d.ts +17 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase2SearchBlock.js +703 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34CloseDetailBlock.d.ts +15 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34CloseDetailBlock.js +41 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34CloseTabsBlock.d.ts +26 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34CloseTabsBlock.js +44 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34CollectCommentsBlock.d.ts +29 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34CollectCommentsBlock.js +150 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34ExtractDetailBlock.d.ts +38 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34ExtractDetailBlock.js +117 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34OpenDetailBlock.d.ts +30 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34OpenDetailBlock.js +102 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34OpenTabsBlock.d.ts +23 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34OpenTabsBlock.js +109 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34PersistDetailBlock.d.ts +32 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34PersistDetailBlock.js +117 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34ProcessSingleNoteBlock.d.ts +35 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34ProcessSingleNoteBlock.js +114 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34ValidateLinksBlock.d.ts +34 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase34ValidateLinksBlock.js +90 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase3InteractBlock.d.ts +111 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase3InteractBlock.js +1009 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase4MultiTabHarvestBlock.d.ts +20 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/Phase4MultiTabHarvestBlock.js +233 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/ReplyInteractBlock.d.ts +48 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/ReplyInteractBlock.js +291 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/XhsDiscoverFallbackBlock.d.ts +23 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/XhsDiscoverFallbackBlock.js +240 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/commentMatchDsl.d.ts +55 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/commentMatchDsl.js +126 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/commentMatcher.d.ts +21 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/commentMatcher.js +99 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/evidence.d.ts +5 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/evidence.js +27 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/sharding.d.ts +37 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/sharding.js +165 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/xhsComments.d.ts +33 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/blocks/helpers/xhsComments.js +270 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/index.d.ts +9 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/index.js +9 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/utils/checkpoints.d.ts +50 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/utils/checkpoints.js +222 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/utils/controllerAction.d.ts +10 -0
- package/dist/modules/xiaohongshu/app/src/xiaohongshu/app/src/utils/controllerAction.js +43 -0
- package/dist/services/shared/serviceProcessLogger.js +1 -1
- package/dist/services/unified-api/server.js +105 -11
- package/modules/camo-backend/src/index.ts +46 -1
- package/modules/camo-backend/src/internal/BrowserSession.ts +619 -3
- package/modules/camo-backend/src/internal/SessionManager.ts +12 -1
- package/modules/camo-backend/src/internal/storage-paths.ts +5 -0
- package/modules/camo-runtime/src/autoscript/action-providers/xhs/comments.mjs +38 -2
- package/modules/camo-runtime/src/autoscript/action-providers/xhs/interaction.mjs +47 -2
- package/modules/camo-runtime/src/autoscript/action-providers/xhs/search.mjs +94 -11
- package/modules/camo-runtime/src/autoscript/action-providers/xhs.mjs +208 -2
- package/modules/camo-runtime/src/autoscript/runtime.mjs +7 -1
- package/modules/camo-runtime/src/autoscript/xhs-unified-template.mjs +76 -43
- package/modules/camo-runtime/src/container/runtime-core/operations/index.mjs +75 -1
- package/modules/camo-runtime/src/container/runtime-core/operations/selector-scripts.mjs +71 -4
- package/modules/camo-runtime/src/container/runtime-core/operations/tab-pool.mjs +183 -27
- package/modules/collection-manager/bloom-filter.ts +112 -0
- package/modules/collection-manager/date-utils.ts +316 -0
- package/modules/collection-manager/index.ts +309 -0
- package/modules/collection-manager/package.json +10 -0
- package/modules/collection-manager/storage.ts +174 -0
- package/modules/collection-manager/types.ts +156 -0
- package/modules/logging/src/index.ts +1 -1
- package/modules/process-registry/index.ts +284 -0
- package/modules/rate-limiter/index.ts +322 -0
- package/modules/state/src/paths.ts +9 -1
- package/modules/task-scheduler/index.ts +293 -0
- package/modules/workflow/blocks/ExecuteWeiboSearchBlock.ts +167 -0
- package/modules/workflow/blocks/PersistXhsNoteBlock.ts +7 -3
- package/modules/workflow/blocks/RenderMarkdown.ts +4 -1
- package/modules/workflow/blocks/WeiboCollectCommentsBlock.ts +339 -0
- package/modules/workflow/blocks/WeiboCollectFromLinksBlock.ts +338 -0
- package/modules/workflow/blocks/helpers/downloadPaths.ts +16 -0
- package/modules/workflow/config/workflowRegistry.ts +2 -0
- package/modules/workflow/definitions/weibo-search-workflow-v1.ts +47 -0
- package/modules/workflow/src/runner.ts +6 -0
- package/modules/xiaohongshu/app/src/blocks/Phase1StartProfileBlock.ts +1 -1
- package/modules/xiaohongshu/app/src/blocks/Phase34PersistDetailBlock.ts +4 -0
- package/modules/xiaohongshu/app/src/blocks/Phase3InteractBlock.ts +2 -3
- package/modules/xiaohongshu/app/src/blocks/helpers/sharding.ts +152 -0
- package/package.json +14 -5
- package/scripts/postinstall-resources.mjs +62 -0
- package/scripts/test/run-coverage.mjs +76 -0
- package/scripts/weibo/search.ts +49 -0
- package/services/shared/serviceProcessLogger.ts +1 -1
- package/services/unified-api/server.ts +98 -12
|
@@ -14,6 +14,7 @@ const DEFAULT_PLATFORM = 'xiaohongshu';
|
|
|
14
14
|
const STATUS_ACTIVE = 'active';
|
|
15
15
|
const STATUS_VALID = 'valid';
|
|
16
16
|
const STATUS_INVALID = 'invalid';
|
|
17
|
+
const STATUS_PENDING = 'pending';
|
|
17
18
|
|
|
18
19
|
function nowIso() {
|
|
19
20
|
return new Date().toISOString();
|
|
@@ -21,7 +22,8 @@ function nowIso() {
|
|
|
21
22
|
|
|
22
23
|
function readJson(filePath, fallback) {
|
|
23
24
|
try {
|
|
24
|
-
|
|
25
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
26
|
+
return JSON.parse(String(raw).replace(/^\uFEFF/, ''));
|
|
25
27
|
} catch {
|
|
26
28
|
return fallback;
|
|
27
29
|
}
|
|
@@ -91,12 +93,21 @@ function formatSeq(seq) {
|
|
|
91
93
|
return String(seq).padStart(4, '0');
|
|
92
94
|
}
|
|
93
95
|
|
|
96
|
+
function resolveAutoTag(platform) {
|
|
97
|
+
const normalized = normalizePlatform(platform);
|
|
98
|
+
return normalized === 'xiaohongshu'
|
|
99
|
+
? 'xhs'
|
|
100
|
+
: (toSlug(normalized).split('-')[0] || 'acct');
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function buildAutoAccountId(platform, seq) {
|
|
104
|
+
return `${resolveAutoTag(platform)}-${formatSeq(seq)}`;
|
|
105
|
+
}
|
|
106
|
+
|
|
94
107
|
function normalizeId(id, fallbackPlatform, seq) {
|
|
95
108
|
const cleaned = toSlug(id || '');
|
|
96
109
|
if (cleaned) return cleaned;
|
|
97
|
-
|
|
98
|
-
const tag = platform === 'xiaohongshu' ? 'xhs' : (toSlug(platform).split('-')[0] || 'acct');
|
|
99
|
-
return `${tag}-${formatSeq(seq)}`;
|
|
110
|
+
return buildAutoAccountId(fallbackPlatform, seq);
|
|
100
111
|
}
|
|
101
112
|
|
|
102
113
|
function ensureSafeName(name, field) {
|
|
@@ -133,6 +144,20 @@ function resolveAccountDir(id) {
|
|
|
133
144
|
return path.join(resolveAccountsRoot(), id);
|
|
134
145
|
}
|
|
135
146
|
|
|
147
|
+
function removeAccountDirById(id) {
|
|
148
|
+
const safeId = String(id || '').trim();
|
|
149
|
+
if (!safeId) return;
|
|
150
|
+
const dir = resolveAccountDir(safeId);
|
|
151
|
+
if (fs.existsSync(dir)) {
|
|
152
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function hasPersistentAccountId(record) {
|
|
157
|
+
if (normalizeText(record?.accountId)) return true;
|
|
158
|
+
return normalizeStatus(record?.status) === STATUS_PENDING;
|
|
159
|
+
}
|
|
160
|
+
|
|
136
161
|
function isWithinDir(rootDir, targetPath) {
|
|
137
162
|
const root = path.resolve(rootDir);
|
|
138
163
|
const target = path.resolve(targetPath);
|
|
@@ -142,7 +167,12 @@ function isWithinDir(rootDir, targetPath) {
|
|
|
142
167
|
function loadIndex() {
|
|
143
168
|
const fallback = { version: 1, nextSeq: 1, updatedAt: null, accounts: [] };
|
|
144
169
|
const raw = readJson(resolveIndexPath(), fallback);
|
|
145
|
-
const
|
|
170
|
+
const accountsRaw = Array.isArray(raw?.accounts) ? raw.accounts : [];
|
|
171
|
+
const staleIds = accountsRaw
|
|
172
|
+
.filter((account) => !hasPersistentAccountId(account))
|
|
173
|
+
.map((account) => String(account?.id || '').trim())
|
|
174
|
+
.filter(Boolean);
|
|
175
|
+
const accounts = accountsRaw.filter((account) => hasPersistentAccountId(account));
|
|
146
176
|
const maxSeq = accounts.reduce((max, account) => {
|
|
147
177
|
const seq = Number(account?.seq);
|
|
148
178
|
return Number.isFinite(seq) ? Math.max(max, seq) : max;
|
|
@@ -150,12 +180,26 @@ function loadIndex() {
|
|
|
150
180
|
const nextSeq = Number.isFinite(Number(raw?.nextSeq)) && Number(raw?.nextSeq) > maxSeq
|
|
151
181
|
? Number(raw.nextSeq)
|
|
152
182
|
: maxSeq + 1;
|
|
153
|
-
|
|
183
|
+
const normalized = {
|
|
154
184
|
version: 1,
|
|
155
185
|
nextSeq,
|
|
156
186
|
updatedAt: raw?.updatedAt || null,
|
|
157
187
|
accounts,
|
|
158
188
|
};
|
|
189
|
+
|
|
190
|
+
if (staleIds.length > 0 || accounts.length !== accountsRaw.length) {
|
|
191
|
+
writeJson(resolveIndexPath(), {
|
|
192
|
+
version: 1,
|
|
193
|
+
nextSeq,
|
|
194
|
+
updatedAt: nowIso(),
|
|
195
|
+
accounts,
|
|
196
|
+
});
|
|
197
|
+
for (const id of staleIds) {
|
|
198
|
+
removeAccountDirById(id);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return normalized;
|
|
159
203
|
}
|
|
160
204
|
|
|
161
205
|
function saveIndex(index) {
|
|
@@ -175,6 +219,43 @@ function resolveProfilePrefix(platform) {
|
|
|
175
219
|
return `${slug}-account`;
|
|
176
220
|
}
|
|
177
221
|
|
|
222
|
+
function resolveProfileSeq(profileId, platform) {
|
|
223
|
+
const value = String(profileId || '').trim();
|
|
224
|
+
if (!value) return null;
|
|
225
|
+
const prefix = resolveProfilePrefix(platform);
|
|
226
|
+
const match = value.match(new RegExp(`^${prefix.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}-([0-9]+)$`));
|
|
227
|
+
if (!match) return null;
|
|
228
|
+
const seq = Number(match[1]);
|
|
229
|
+
if (!Number.isFinite(seq) || seq < 0) return null;
|
|
230
|
+
return seq;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
function resolveUsedAutoSeq(index, platform) {
|
|
234
|
+
const tag = resolveAutoTag(platform);
|
|
235
|
+
const pattern = new RegExp(`^${tag}-([0-9]+)$`);
|
|
236
|
+
const used = new Set();
|
|
237
|
+
for (const row of (index?.accounts || [])) {
|
|
238
|
+
const id = String(row?.id || '').trim();
|
|
239
|
+
const match = id.match(pattern);
|
|
240
|
+
if (!match) continue;
|
|
241
|
+
const seq = Number(match[1]);
|
|
242
|
+
if (!Number.isFinite(seq) || seq < 0) continue;
|
|
243
|
+
used.add(seq);
|
|
244
|
+
}
|
|
245
|
+
return used;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function resolveNextAutoSeq(index, platform, preferredSeq = null) {
|
|
249
|
+
const used = resolveUsedAutoSeq(index, platform);
|
|
250
|
+
const preferred = Number(preferredSeq);
|
|
251
|
+
if (Number.isFinite(preferred) && preferred >= 0 && !used.has(preferred)) {
|
|
252
|
+
return preferred;
|
|
253
|
+
}
|
|
254
|
+
let seq = 0;
|
|
255
|
+
while (used.has(seq)) seq += 1;
|
|
256
|
+
return seq;
|
|
257
|
+
}
|
|
258
|
+
|
|
178
259
|
function ensureAliasUnique(accounts, alias, exceptId = '') {
|
|
179
260
|
if (!alias) return;
|
|
180
261
|
const target = alias.toLowerCase();
|
|
@@ -227,10 +308,7 @@ function persistAccountMeta(account) {
|
|
|
227
308
|
}
|
|
228
309
|
|
|
229
310
|
function deleteAccountMeta(id) {
|
|
230
|
-
|
|
231
|
-
if (fs.existsSync(dir)) {
|
|
232
|
-
fs.rmSync(dir, { recursive: true, force: true });
|
|
233
|
-
}
|
|
311
|
+
removeAccountDirById(id);
|
|
234
312
|
}
|
|
235
313
|
|
|
236
314
|
function resolveAccountName(inputName, platform, seq) {
|
|
@@ -249,9 +327,11 @@ function buildProfileAccountView(profileId, record = null) {
|
|
|
249
327
|
const accountId = normalizeText(record?.accountId);
|
|
250
328
|
const status = normalizeStatus(record?.status || (accountId ? STATUS_VALID : STATUS_INVALID));
|
|
251
329
|
const valid = status === STATUS_VALID && Boolean(accountId);
|
|
330
|
+
const platform = normalizePlatform(record?.platform);
|
|
252
331
|
return {
|
|
253
332
|
profileId,
|
|
254
333
|
accountRecordId: record?.id || null,
|
|
334
|
+
platform,
|
|
255
335
|
accountId,
|
|
256
336
|
alias: normalizeText(record?.alias),
|
|
257
337
|
name: normalizeText(record?.name),
|
|
@@ -305,9 +385,29 @@ export function getAccount(idOrAlias) {
|
|
|
305
385
|
|
|
306
386
|
export async function addAccount(input = {}) {
|
|
307
387
|
const index = loadIndex();
|
|
308
|
-
const seq = Number(index.nextSeq) || 1;
|
|
309
388
|
const platform = normalizePlatform(input.platform);
|
|
310
|
-
const
|
|
389
|
+
const hasCustomId = Boolean(normalizeText(input.id));
|
|
390
|
+
const explicitProfileId = normalizeText(input.profileId);
|
|
391
|
+
const autoPrefix = resolveProfilePrefix(platform);
|
|
392
|
+
let seq = null;
|
|
393
|
+
let profileId = explicitProfileId;
|
|
394
|
+
|
|
395
|
+
if (!hasCustomId && !explicitProfileId) {
|
|
396
|
+
// Default path: account/profile share the same minimal available slot.
|
|
397
|
+
seq = resolveNextAutoSeq(index, platform, null);
|
|
398
|
+
profileId = `${autoPrefix}-${seq}`;
|
|
399
|
+
} else {
|
|
400
|
+
profileId = explicitProfileId || resolveNextProfileId(autoPrefix);
|
|
401
|
+
const profileSeq = resolveProfileSeq(profileId, platform);
|
|
402
|
+
seq = resolveNextAutoSeq(index, platform, hasCustomId ? null : profileSeq);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
await ensureProfile(profileId);
|
|
406
|
+
|
|
407
|
+
const id = ensureSafeName(
|
|
408
|
+
hasCustomId ? normalizeId(input.id, platform, seq) : buildAutoAccountId(platform, seq),
|
|
409
|
+
'id',
|
|
410
|
+
);
|
|
311
411
|
if (index.accounts.some((item) => item?.id === id)) {
|
|
312
412
|
throw new Error(`account id already exists: ${id}`);
|
|
313
413
|
}
|
|
@@ -315,21 +415,24 @@ export async function addAccount(input = {}) {
|
|
|
315
415
|
const alias = normalizeAlias(input.alias) || normalizeAlias(input.username);
|
|
316
416
|
ensureAliasUnique(index.accounts, alias);
|
|
317
417
|
|
|
318
|
-
const profileId = normalizeText(input.profileId)
|
|
319
|
-
|| resolveNextProfileId(resolveProfilePrefix(platform));
|
|
320
|
-
await ensureProfile(profileId);
|
|
321
|
-
|
|
322
418
|
const fingerprintId = normalizeText(input.fingerprintId) || profileId;
|
|
323
419
|
const createdAt = nowIso();
|
|
324
420
|
const accountId = normalizeText(input.accountId || input.platformAccountId || null);
|
|
325
|
-
const
|
|
421
|
+
const requestedStatus = normalizeStatus(input.status);
|
|
422
|
+
const status = accountId
|
|
423
|
+
? STATUS_VALID
|
|
424
|
+
: (requestedStatus === STATUS_PENDING ? STATUS_PENDING : STATUS_INVALID);
|
|
425
|
+
const reason =
|
|
426
|
+
status === STATUS_VALID
|
|
427
|
+
? null
|
|
428
|
+
: (status === STATUS_PENDING ? 'waiting_login' : 'missing_account_id');
|
|
326
429
|
const account = {
|
|
327
430
|
id,
|
|
328
431
|
seq,
|
|
329
432
|
platform,
|
|
330
433
|
status,
|
|
331
|
-
valid:
|
|
332
|
-
reason
|
|
434
|
+
valid: false,
|
|
435
|
+
reason,
|
|
333
436
|
accountId,
|
|
334
437
|
name: accountId || resolveAccountName(input.name, platform, seq),
|
|
335
438
|
alias,
|
|
@@ -342,7 +445,7 @@ export async function addAccount(input = {}) {
|
|
|
342
445
|
};
|
|
343
446
|
|
|
344
447
|
index.accounts.push(account);
|
|
345
|
-
index.nextSeq = seq + 1;
|
|
448
|
+
index.nextSeq = Math.max(Number(index.nextSeq) || 1, seq + 1);
|
|
346
449
|
saveIndex(index);
|
|
347
450
|
persistAccountMeta(account);
|
|
348
451
|
|
|
@@ -406,9 +509,14 @@ export async function updateAccount(idOrAlias, patch = {}) {
|
|
|
406
509
|
next.name = String(next.accountId);
|
|
407
510
|
}
|
|
408
511
|
if (!next.accountId) {
|
|
409
|
-
next.status
|
|
410
|
-
|
|
411
|
-
|
|
512
|
+
if (next.status === STATUS_PENDING) {
|
|
513
|
+
next.valid = false;
|
|
514
|
+
if (!next.reason) next.reason = 'waiting_login';
|
|
515
|
+
} else {
|
|
516
|
+
next.status = STATUS_INVALID;
|
|
517
|
+
next.valid = false;
|
|
518
|
+
if (!next.reason) next.reason = 'missing_account_id';
|
|
519
|
+
}
|
|
412
520
|
} else if (next.status !== 'disabled' && next.status !== 'archived') {
|
|
413
521
|
next.status = next.valid === false ? STATUS_INVALID : STATUS_VALID;
|
|
414
522
|
}
|
|
@@ -418,6 +526,9 @@ export async function updateAccount(idOrAlias, patch = {}) {
|
|
|
418
526
|
} else if (next.status === STATUS_INVALID) {
|
|
419
527
|
next.valid = false;
|
|
420
528
|
next.reason = next.reason || 'invalid';
|
|
529
|
+
} else if (next.status === STATUS_PENDING) {
|
|
530
|
+
next.valid = false;
|
|
531
|
+
next.reason = next.reason || 'waiting_login';
|
|
421
532
|
}
|
|
422
533
|
|
|
423
534
|
next.updatedAt = nowIso();
|
|
@@ -437,7 +548,9 @@ export function upsertProfileAccountState(input = {}) {
|
|
|
437
548
|
const alias = normalizeAlias(input.alias);
|
|
438
549
|
const reason = normalizeText(input.reason);
|
|
439
550
|
const detectedAt = normalizeText(input.detectedAt) || nowIso();
|
|
440
|
-
const
|
|
551
|
+
const statusHint = normalizeStatus(input.status);
|
|
552
|
+
const pendingMode = !accountId && statusHint === STATUS_PENDING;
|
|
553
|
+
const status = accountId ? STATUS_VALID : (pendingMode ? STATUS_PENDING : STATUS_INVALID);
|
|
441
554
|
|
|
442
555
|
const index = loadIndex();
|
|
443
556
|
const existingByProfile = resolveAccountByProfile(index, profileId);
|
|
@@ -445,20 +558,86 @@ export function upsertProfileAccountState(input = {}) {
|
|
|
445
558
|
let target = existingByAccountId || existingByProfile || null;
|
|
446
559
|
const purgeIds = new Set();
|
|
447
560
|
|
|
448
|
-
if (!
|
|
561
|
+
if (!accountId) {
|
|
562
|
+
if (target && hasPersistentAccountId(target)) {
|
|
563
|
+
const nextStatus = pendingMode ? STATUS_PENDING : STATUS_INVALID;
|
|
564
|
+
const next = {
|
|
565
|
+
...target,
|
|
566
|
+
platform,
|
|
567
|
+
profileId,
|
|
568
|
+
fingerprintId: profileId,
|
|
569
|
+
status: nextStatus,
|
|
570
|
+
valid: false,
|
|
571
|
+
reason: reason || (nextStatus === STATUS_PENDING ? 'waiting_login' : 'invalid'),
|
|
572
|
+
detectedAt,
|
|
573
|
+
updatedAt: nowIso(),
|
|
574
|
+
};
|
|
575
|
+
const rowIndex = index.accounts.findIndex((item) => item?.id === target.id);
|
|
576
|
+
if (rowIndex < 0) throw new Error(`account not found: ${target.id}`);
|
|
577
|
+
index.accounts[rowIndex] = next;
|
|
578
|
+
saveIndex(index);
|
|
579
|
+
persistAccountMeta(next);
|
|
580
|
+
return buildProfileAccountView(profileId, next);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
if (pendingMode) {
|
|
584
|
+
const profileSeq = resolveProfileSeq(profileId, platform);
|
|
585
|
+
const seq = resolveNextAutoSeq(index, platform, profileSeq);
|
|
586
|
+
const id = ensureSafeName(buildAutoAccountId(platform, seq), 'id');
|
|
587
|
+
const createdAt = nowIso();
|
|
588
|
+
const record = {
|
|
589
|
+
id,
|
|
590
|
+
seq,
|
|
591
|
+
platform,
|
|
592
|
+
status: STATUS_PENDING,
|
|
593
|
+
valid: false,
|
|
594
|
+
reason: reason || 'waiting_login',
|
|
595
|
+
accountId: null,
|
|
596
|
+
name: `${platform}-${profileId}`,
|
|
597
|
+
alias: alias || null,
|
|
598
|
+
username: null,
|
|
599
|
+
profileId,
|
|
600
|
+
fingerprintId: profileId,
|
|
601
|
+
createdAt,
|
|
602
|
+
updatedAt: createdAt,
|
|
603
|
+
detectedAt,
|
|
604
|
+
aliasSource: alias ? 'auto' : null,
|
|
605
|
+
};
|
|
606
|
+
index.accounts.push(record);
|
|
607
|
+
index.nextSeq = Math.max(Number(index.nextSeq) || 1, seq + 1);
|
|
608
|
+
saveIndex(index);
|
|
609
|
+
persistAccountMeta(record);
|
|
610
|
+
return buildProfileAccountView(profileId, record);
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
const staleIds = index.accounts
|
|
614
|
+
.filter((item) => String(item?.profileId || '').trim() === profileId && !hasPersistentAccountId(item))
|
|
615
|
+
.map((item) => String(item?.id || '').trim())
|
|
616
|
+
.filter(Boolean);
|
|
617
|
+
if (staleIds.length > 0) {
|
|
618
|
+
index.accounts = index.accounts.filter((item) => {
|
|
619
|
+
const id = String(item?.id || '').trim();
|
|
620
|
+
return !staleIds.includes(id);
|
|
621
|
+
});
|
|
622
|
+
saveIndex(index);
|
|
623
|
+
for (const id of staleIds) deleteAccountMeta(id);
|
|
624
|
+
}
|
|
625
|
+
|
|
449
626
|
return buildProfileAccountView(profileId, {
|
|
450
627
|
profileId,
|
|
451
628
|
accountId: null,
|
|
452
|
-
|
|
629
|
+
alias: null,
|
|
630
|
+
status,
|
|
453
631
|
valid: false,
|
|
454
|
-
reason: reason || 'missing_account_id',
|
|
632
|
+
reason: reason || (pendingMode ? 'waiting_login' : 'missing_account_id'),
|
|
455
633
|
updatedAt: detectedAt,
|
|
456
634
|
});
|
|
457
635
|
}
|
|
458
636
|
|
|
459
637
|
if (!target) {
|
|
460
|
-
const
|
|
461
|
-
const
|
|
638
|
+
const profileSeq = resolveProfileSeq(profileId, platform);
|
|
639
|
+
const seq = resolveNextAutoSeq(index, platform, profileSeq);
|
|
640
|
+
const id = ensureSafeName(buildAutoAccountId(platform, seq), 'id');
|
|
462
641
|
const createdAt = nowIso();
|
|
463
642
|
const record = {
|
|
464
643
|
id,
|
|
@@ -479,7 +658,7 @@ export function upsertProfileAccountState(input = {}) {
|
|
|
479
658
|
aliasSource: alias ? 'auto' : null,
|
|
480
659
|
};
|
|
481
660
|
index.accounts.push(record);
|
|
482
|
-
index.nextSeq = seq + 1;
|
|
661
|
+
index.nextSeq = Math.max(Number(index.nextSeq) || 1, seq + 1);
|
|
483
662
|
saveIndex(index);
|
|
484
663
|
persistAccountMeta(record);
|
|
485
664
|
return buildProfileAccountView(profileId, record);
|
|
@@ -553,6 +732,16 @@ export function markProfileInvalid(profileId, reason = 'login_guard') {
|
|
|
553
732
|
});
|
|
554
733
|
}
|
|
555
734
|
|
|
735
|
+
export function markProfilePending(profileId, reason = 'waiting_login') {
|
|
736
|
+
const id = ensureSafeName(normalizeText(profileId), 'profileId');
|
|
737
|
+
return upsertProfileAccountState({
|
|
738
|
+
profileId: id,
|
|
739
|
+
accountId: null,
|
|
740
|
+
status: STATUS_PENDING,
|
|
741
|
+
reason,
|
|
742
|
+
});
|
|
743
|
+
}
|
|
744
|
+
|
|
556
745
|
export function removeAccount(idOrAlias, options = {}) {
|
|
557
746
|
const index = loadIndex();
|
|
558
747
|
const account = resolveAccountOrThrow(index, idOrAlias);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import WebSocket from 'ws';
|
|
2
|
+
|
|
3
|
+
function resolveBusUrl() {
|
|
4
|
+
const explicit = String(process.env.WEBAUTO_UNIFIED_BUS_URL || '').trim();
|
|
5
|
+
if (explicit) return explicit;
|
|
6
|
+
const host = String(process.env.WEBAUTO_UNIFIED_HOST || '127.0.0.1').trim() || '127.0.0.1';
|
|
7
|
+
const port = Number(
|
|
8
|
+
process.env.WEBAUTO_FLOATING_BUS_PORT
|
|
9
|
+
|| process.env.WEBAUTO_UNIFIED_PORT
|
|
10
|
+
|| 7701,
|
|
11
|
+
);
|
|
12
|
+
return `ws://${host}:${Number.isFinite(port) ? port : 7701}/bus`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function publishBusEvent(payload, options = {}) {
|
|
16
|
+
const message = typeof payload === 'string' ? payload : JSON.stringify(payload ?? {});
|
|
17
|
+
const timeoutMs = Math.max(300, Number(options.timeoutMs || 1500));
|
|
18
|
+
const url = resolveBusUrl();
|
|
19
|
+
|
|
20
|
+
return await new Promise((resolve) => {
|
|
21
|
+
let settled = false;
|
|
22
|
+
let sent = false;
|
|
23
|
+
let ws;
|
|
24
|
+
|
|
25
|
+
const done = (ok) => {
|
|
26
|
+
if (settled) return;
|
|
27
|
+
settled = true;
|
|
28
|
+
resolve(ok);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
ws = new WebSocket(url);
|
|
33
|
+
} catch {
|
|
34
|
+
done(false);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const timer = setTimeout(() => {
|
|
39
|
+
try { ws?.terminate(); } catch {}
|
|
40
|
+
done(false);
|
|
41
|
+
}, timeoutMs);
|
|
42
|
+
|
|
43
|
+
ws.on('open', () => {
|
|
44
|
+
try {
|
|
45
|
+
ws.send(message);
|
|
46
|
+
sent = true;
|
|
47
|
+
} catch {
|
|
48
|
+
// ignore send failure
|
|
49
|
+
}
|
|
50
|
+
setTimeout(() => {
|
|
51
|
+
try { ws.close(); } catch {}
|
|
52
|
+
}, 30);
|
|
53
|
+
});
|
|
54
|
+
ws.on('close', () => {
|
|
55
|
+
clearTimeout(timer);
|
|
56
|
+
done(sent);
|
|
57
|
+
});
|
|
58
|
+
ws.on('error', () => {
|
|
59
|
+
clearTimeout(timer);
|
|
60
|
+
done(false);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
|
|
5
|
+
function resolveOnPath(candidates) {
|
|
6
|
+
const pathEnv = process.env.PATH || process.env.Path || '';
|
|
7
|
+
const dirs = pathEnv.split(path.delimiter).filter(Boolean);
|
|
8
|
+
for (const dir of dirs) {
|
|
9
|
+
for (const name of candidates) {
|
|
10
|
+
const full = path.join(dir, name);
|
|
11
|
+
if (existsSync(full)) return full;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function resolveInDir(dir, candidates) {
|
|
18
|
+
for (const name of candidates) {
|
|
19
|
+
const full = path.join(dir, name);
|
|
20
|
+
if (existsSync(full)) return full;
|
|
21
|
+
}
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function wrapWindowsRunner(cmdPath, prefix = []) {
|
|
26
|
+
if (process.platform !== 'win32') return { cmd: cmdPath, prefix };
|
|
27
|
+
const lower = String(cmdPath || '').toLowerCase();
|
|
28
|
+
if (lower.endsWith('.ps1')) {
|
|
29
|
+
return {
|
|
30
|
+
cmd: 'powershell.exe',
|
|
31
|
+
prefix: ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', cmdPath, ...prefix],
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
if (lower.endsWith('.cmd') || lower.endsWith('.bat')) {
|
|
35
|
+
return {
|
|
36
|
+
cmd: 'cmd.exe',
|
|
37
|
+
prefix: ['/d', '/s', '/c', cmdPath, ...prefix],
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
return { cmd: cmdPath, prefix };
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function getCamoRunner(rootDir = process.cwd()) {
|
|
44
|
+
const isWin = process.platform === 'win32';
|
|
45
|
+
const localBin = path.join(rootDir, 'node_modules', '.bin');
|
|
46
|
+
const camoNames = isWin ? ['camo.cmd', 'camo.exe', 'camo.bat', 'camo.ps1'] : ['camo'];
|
|
47
|
+
const npxNames = isWin ? ['npx.cmd', 'npx.exe', 'npx.bat', 'npx.ps1'] : ['npx'];
|
|
48
|
+
|
|
49
|
+
const local = resolveInDir(localBin, camoNames);
|
|
50
|
+
if (local) return wrapWindowsRunner(local);
|
|
51
|
+
|
|
52
|
+
const global = resolveOnPath(camoNames);
|
|
53
|
+
if (global) return wrapWindowsRunner(global);
|
|
54
|
+
|
|
55
|
+
const npx = resolveOnPath(npxNames) || (isWin ? 'npx.cmd' : 'npx');
|
|
56
|
+
return wrapWindowsRunner(npx, ['--yes', '--package=@web-auto/camo', 'camo']);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function parseLastJson(stdout) {
|
|
60
|
+
const text = String(stdout || '').trim();
|
|
61
|
+
if (!text) return null;
|
|
62
|
+
const lines = text.split('\n').map((line) => line.trim()).filter(Boolean).reverse();
|
|
63
|
+
for (const line of lines) {
|
|
64
|
+
try {
|
|
65
|
+
return JSON.parse(line);
|
|
66
|
+
} catch {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function runCamo(args, options = {}) {
|
|
74
|
+
const rootDir = String(options.rootDir || process.cwd());
|
|
75
|
+
const timeoutMs = Number(options.timeoutMs) > 0 ? Number(options.timeoutMs) : 60000;
|
|
76
|
+
const runner = getCamoRunner(rootDir);
|
|
77
|
+
const ret = spawnSync(runner.cmd, [...runner.prefix, ...args], {
|
|
78
|
+
cwd: rootDir,
|
|
79
|
+
env: { ...process.env, ...(options.env || {}) },
|
|
80
|
+
encoding: 'utf8',
|
|
81
|
+
timeout: timeoutMs,
|
|
82
|
+
windowsHide: true,
|
|
83
|
+
});
|
|
84
|
+
const stdout = String(ret.stdout || '').trim();
|
|
85
|
+
const stderr = String(ret.stderr || '').trim();
|
|
86
|
+
return {
|
|
87
|
+
ok: ret.status === 0,
|
|
88
|
+
code: ret.status,
|
|
89
|
+
stdout,
|
|
90
|
+
stderr,
|
|
91
|
+
json: parseLastJson(stdout),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -7,12 +7,19 @@ function resolvePortableRoot() {
|
|
|
7
7
|
return root ? path.join(root, '.webauto') : '';
|
|
8
8
|
}
|
|
9
9
|
|
|
10
|
+
function resolveHomeDir() {
|
|
11
|
+
if (process.platform === 'win32') {
|
|
12
|
+
return process.env.USERPROFILE || os.homedir();
|
|
13
|
+
}
|
|
14
|
+
return process.env.HOME || os.homedir();
|
|
15
|
+
}
|
|
16
|
+
|
|
10
17
|
export function resolveProfilesRoot() {
|
|
11
18
|
const envProfiles = String(process.env.WEBAUTO_PATHS_PROFILES || '').trim();
|
|
12
19
|
if (envProfiles) return envProfiles;
|
|
13
20
|
const portableRoot = resolvePortableRoot();
|
|
14
21
|
if (portableRoot) return path.join(portableRoot, 'profiles');
|
|
15
|
-
return path.join(
|
|
22
|
+
return path.join(resolveHomeDir(), '.webauto', 'profiles');
|
|
16
23
|
}
|
|
17
24
|
|
|
18
25
|
export function resolveFingerprintsRoot() {
|
|
@@ -20,7 +27,7 @@ export function resolveFingerprintsRoot() {
|
|
|
20
27
|
if (envFps) return envFps;
|
|
21
28
|
const portableRoot = resolvePortableRoot();
|
|
22
29
|
if (portableRoot) return path.join(portableRoot, 'fingerprints');
|
|
23
|
-
return path.join(
|
|
30
|
+
return path.join(resolveHomeDir(), '.webauto', 'fingerprints');
|
|
24
31
|
}
|
|
25
32
|
|
|
26
33
|
export function listProfiles() {
|
|
@@ -52,14 +59,16 @@ export function resolveNextProfileId(prefix) {
|
|
|
52
59
|
const normalized = String(prefix || '').trim();
|
|
53
60
|
if (!normalized) throw new Error('prefix is required');
|
|
54
61
|
const { profiles } = listProfilesForPool(normalized);
|
|
55
|
-
|
|
62
|
+
const used = new Set();
|
|
56
63
|
for (const profileId of profiles) {
|
|
57
64
|
const match = profileId.match(/-(\d+)$/);
|
|
58
65
|
if (!match) continue;
|
|
59
66
|
const index = Number(match[1]);
|
|
60
|
-
if (Number.isFinite(index)
|
|
67
|
+
if (Number.isFinite(index) && index >= 0) used.add(index);
|
|
61
68
|
}
|
|
62
|
-
|
|
69
|
+
let next = 0;
|
|
70
|
+
while (used.has(next)) next += 1;
|
|
71
|
+
return `${normalized}-${next}`;
|
|
63
72
|
}
|
|
64
73
|
|
|
65
74
|
export async function ensureProfile(profileId) {
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* quota-status.mjs - CLI tool to get rate limiter status
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { RateLimiter } from '../../../../dist/modules/rate-limiter/index.js';
|
|
7
|
+
|
|
8
|
+
async function main() {
|
|
9
|
+
const limiter = RateLimiter.getInstance();
|
|
10
|
+
await limiter.init();
|
|
11
|
+
|
|
12
|
+
const status = limiter.getStatus();
|
|
13
|
+
|
|
14
|
+
console.log(JSON.stringify({
|
|
15
|
+
ok: true,
|
|
16
|
+
quotas: status
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
main().catch(err => {
|
|
21
|
+
console.error(JSON.stringify({ ok: false, error: err.message }));
|
|
22
|
+
process.exit(1);
|
|
23
|
+
});
|