@optima-chat/browser-cli 0.7.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +84 -68
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -174,10 +174,9 @@ async function requestBinary(path) {
|
|
|
174
174
|
return Buffer.from(arrayBuffer);
|
|
175
175
|
}
|
|
176
176
|
var BrowserAPI = class {
|
|
177
|
-
async createSession(url,
|
|
177
|
+
async createSession(url, force, connector, ziniaoProfileId, bitbrowserProfileId) {
|
|
178
178
|
const body = {};
|
|
179
179
|
if (url) body.url = url;
|
|
180
|
-
if (profileId) body.profile_id = profileId;
|
|
181
180
|
if (force) body.force = true;
|
|
182
181
|
if (connector) body.connector = connector;
|
|
183
182
|
if (ziniaoProfileId) body.ziniao_profile_id = ziniaoProfileId;
|
|
@@ -187,14 +186,6 @@ var BrowserAPI = class {
|
|
|
187
186
|
body: JSON.stringify(body)
|
|
188
187
|
});
|
|
189
188
|
}
|
|
190
|
-
async listProfiles() {
|
|
191
|
-
return request("/api/profiles");
|
|
192
|
-
}
|
|
193
|
-
async deleteProfile(profileId) {
|
|
194
|
-
return request("/api/profiles/" + encodeURIComponent(profileId), {
|
|
195
|
-
method: "DELETE"
|
|
196
|
-
});
|
|
197
|
-
}
|
|
198
189
|
async setZiNiaoConfig(company, username, password) {
|
|
199
190
|
return request("/api/user/ziniao-config", {
|
|
200
191
|
method: "PUT",
|
|
@@ -258,22 +249,29 @@ var BrowserAPI = class {
|
|
|
258
249
|
async getScreenshot(sessionId) {
|
|
259
250
|
return requestBinary(`/api/sessions/${sessionId}/screenshot`);
|
|
260
251
|
}
|
|
261
|
-
async runRecipeScript(name, params, pollMs = 2e3) {
|
|
252
|
+
async runRecipeScript(name, params, pollMs = 2e3, timeoutMs = 36e4) {
|
|
262
253
|
const started = await request(`/api/scripts/${name}/run`, {
|
|
263
254
|
method: "POST",
|
|
264
255
|
body: JSON.stringify({ params })
|
|
265
256
|
});
|
|
257
|
+
const deadline = Date.now() + timeoutMs;
|
|
266
258
|
for (; ; ) {
|
|
267
|
-
const task = await request(
|
|
268
|
-
`/api/script-tasks/${started.id}`
|
|
269
|
-
);
|
|
259
|
+
const task = await request(`/api/script-tasks/${started.id}`);
|
|
270
260
|
if (task.status === "completed") return task.output;
|
|
271
261
|
if (task.status === "failed" || task.status === "needs_fallback") {
|
|
272
|
-
throw new Error(task.error || `recipe ${task.status}`);
|
|
262
|
+
throw new Error(task.error || task.output?.error || `recipe ${task.status}`);
|
|
263
|
+
}
|
|
264
|
+
if (Date.now() >= deadline) {
|
|
265
|
+
throw new Error(
|
|
266
|
+
`recipe ${name} did not finish within ${Math.round(timeoutMs / 1e3)}s (task ${started.id})`
|
|
267
|
+
);
|
|
273
268
|
}
|
|
274
269
|
await new Promise((r) => setTimeout(r, pollMs));
|
|
275
270
|
}
|
|
276
271
|
}
|
|
272
|
+
async fetchArtifact(artifactPath) {
|
|
273
|
+
return requestBinary(artifactPath);
|
|
274
|
+
}
|
|
277
275
|
async search(sessionId, pattern) {
|
|
278
276
|
return request(`/api/sessions/${sessionId}/search`, {
|
|
279
277
|
method: "POST",
|
|
@@ -328,10 +326,10 @@ var BrowserAPI = class {
|
|
|
328
326
|
async deleteScript(name) {
|
|
329
327
|
await request(`/api/scripts/${encodeURIComponent(name)}`, { method: "DELETE" });
|
|
330
328
|
}
|
|
331
|
-
async runScript(scriptName, params = {},
|
|
329
|
+
async runScript(scriptName, params = {}, connector, ziniaoProfileId, bitbrowserProfileId) {
|
|
332
330
|
const payload = { params };
|
|
333
|
-
if (profileId) payload.profile_id = profileId;
|
|
334
331
|
if (connector) payload.connector = connector;
|
|
332
|
+
if (ziniaoProfileId) payload.ziniao_profile_id = ziniaoProfileId;
|
|
335
333
|
if (bitbrowserProfileId) payload.bitbrowser_profile_id = bitbrowserProfileId;
|
|
336
334
|
return request(`/api/scripts/${encodeURIComponent(scriptName)}/run`, {
|
|
337
335
|
method: "POST",
|
|
@@ -446,26 +444,12 @@ function formatError(err) {
|
|
|
446
444
|
function formatTabsInline(tabs) {
|
|
447
445
|
return tabs.map((t) => `[${t.tab_id}] ${t.title}`).join(", ");
|
|
448
446
|
}
|
|
449
|
-
function formatProfiles(result) {
|
|
450
|
-
if (result.profiles.length === 0) {
|
|
451
|
-
return "No browser profiles found.";
|
|
452
|
-
}
|
|
453
|
-
const lines = result.profiles.map((p) => {
|
|
454
|
-
const sizeMb = (p.size_bytes / (1024 * 1024)).toFixed(1);
|
|
455
|
-
return ` ${p.profile_id} (${sizeMb} MB)`;
|
|
456
|
-
});
|
|
457
|
-
return `Browser profiles:
|
|
458
|
-
${lines.join("\n")}`;
|
|
459
|
-
}
|
|
460
|
-
function formatProfileDelete(profileId) {
|
|
461
|
-
return `Profile '${profileId}' deleted.`;
|
|
462
|
-
}
|
|
463
447
|
|
|
464
448
|
// src/commands/lifecycle.ts
|
|
465
449
|
function registerLifecycleCommands(program2) {
|
|
466
|
-
program2.command("launch").description("Launch a browser session").option("--url <url>", "Navigate to URL after launch").option("--
|
|
450
|
+
program2.command("launch").description("Launch a browser session").option("--url <url>", "Navigate to URL after launch").option("--force", "Close existing session before launching").option("--connector <type>", "Browser connector: chrome, ziniao, or bitbrowser").option("--ziniao-profile <id>", "ZiNiao browser profile ID").option("--bitbrowser-profile <id>", "BitBrowser profile ID").action(async (opts) => {
|
|
467
451
|
try {
|
|
468
|
-
const state = await api.createSession(opts.url, opts.
|
|
452
|
+
const state = await api.createSession(opts.url, opts.force, opts.connector, opts.ziniaoProfile, opts.bitbrowserProfile);
|
|
469
453
|
saveSessionId(state.session_id);
|
|
470
454
|
console.log(formatLaunch(state));
|
|
471
455
|
} catch (err) {
|
|
@@ -682,15 +666,38 @@ function registerTabCommands(program2) {
|
|
|
682
666
|
}
|
|
683
667
|
|
|
684
668
|
// src/commands/script.ts
|
|
685
|
-
import { readFileSync as readFileSync3 } from "fs";
|
|
669
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "fs";
|
|
670
|
+
import { basename, dirname, resolve } from "path";
|
|
686
671
|
var POLL_INTERVAL_MS = 2e3;
|
|
687
672
|
var POLL_TIMEOUT_MS = 18e4;
|
|
673
|
+
var REQUIRED_ARG_FLAG = {
|
|
674
|
+
ziniao_profile_id: "--ziniao-profile <id>",
|
|
675
|
+
bitbrowser_profile_id: "--bitbrowser-profile <id>"
|
|
676
|
+
};
|
|
677
|
+
function formatRequiredArgs(s) {
|
|
678
|
+
if (!s.required_args?.length) return null;
|
|
679
|
+
const flags = s.required_args.map((a) => REQUIRED_ARG_FLAG[a] ?? `--param ${a}=<value>`).join(" ");
|
|
680
|
+
return ` requires: ${flags}`;
|
|
681
|
+
}
|
|
682
|
+
async function deliverArtifact(task, out) {
|
|
683
|
+
const output = task.output;
|
|
684
|
+
if (!output) return null;
|
|
685
|
+
if (output.path) return output.path;
|
|
686
|
+
if (!output.artifact_path) return null;
|
|
687
|
+
const target = resolve(out ?? `./${basename(output.filename ?? "artifact")}`);
|
|
688
|
+
const content = await api.fetchArtifact(output.artifact_path);
|
|
689
|
+
mkdirSync2(dirname(target), { recursive: true });
|
|
690
|
+
writeFileSync3(target, content);
|
|
691
|
+
return target;
|
|
692
|
+
}
|
|
688
693
|
function formatScriptList(scripts) {
|
|
689
694
|
if (scripts.length === 0) return "No scripts registered.";
|
|
690
695
|
const lines = [];
|
|
691
696
|
for (const s of scripts) {
|
|
692
697
|
const tag = s.source ? ` [${s.source}]` : "";
|
|
693
698
|
lines.push(`${s.name} \u2014 ${s.description}${tag}`);
|
|
699
|
+
const requires = formatRequiredArgs(s);
|
|
700
|
+
if (requires) lines.push(requires);
|
|
694
701
|
for (let i = 0; i < s.steps.length; i++) {
|
|
695
702
|
lines.push(` [${i + 1}] ${s.steps[i].id} \u2014 ${s.steps[i].description}`);
|
|
696
703
|
}
|
|
@@ -781,7 +788,7 @@ function registerScriptCommands(program2) {
|
|
|
781
788
|
process.exit(1);
|
|
782
789
|
}
|
|
783
790
|
});
|
|
784
|
-
program2.command("runscript").argument("<name>", "Script name").description("Run a script (polls until complete)").option("--param <params...>", "Parameters as key=value").option("--
|
|
791
|
+
program2.command("runscript").argument("<name>", "Script name").description("Run a script (polls until complete)").option("--param <params...>", "Parameters as key=value").option("--connector <type>", "Browser connector: chrome, ziniao, or bitbrowser").option("--ziniao-profile <id>", "ZiNiao browser profile ID (with --connector ziniao)").option("--bitbrowser-profile <id>", "BitBrowser profile ID (with --connector bitbrowser)").option("--out <path>", "Where to write the script's exported file (default: ./<filename>)").option("--no-poll", "Return immediately without polling").action(async (name, opts) => {
|
|
785
792
|
try {
|
|
786
793
|
const params = {};
|
|
787
794
|
if (opts.param) {
|
|
@@ -790,8 +797,9 @@ function registerScriptCommands(program2) {
|
|
|
790
797
|
params[k] = rest.join("=");
|
|
791
798
|
}
|
|
792
799
|
}
|
|
800
|
+
if (opts.out) params.out_path = resolve(opts.out);
|
|
793
801
|
clearSessionId();
|
|
794
|
-
const task = await api.runScript(name, params, opts.
|
|
802
|
+
const task = await api.runScript(name, params, opts.connector, opts.ziniaoProfile, opts.bitbrowserProfile);
|
|
795
803
|
console.log(`Script started (task: ${task.id})`);
|
|
796
804
|
if (!opts.poll) {
|
|
797
805
|
console.log(`Poll with: browser-cli script-task ${task.id}`);
|
|
@@ -812,6 +820,18 @@ function registerScriptCommands(program2) {
|
|
|
812
820
|
saveSessionId(result.session_id);
|
|
813
821
|
}
|
|
814
822
|
console.log(formatScriptTask(result));
|
|
823
|
+
if (result.status === "completed") {
|
|
824
|
+
try {
|
|
825
|
+
const saved = await deliverArtifact(result, opts.out);
|
|
826
|
+
if (saved) console.log(`Saved -> ${saved}`);
|
|
827
|
+
} catch (err) {
|
|
828
|
+
const staged = result.output?.artifact_path;
|
|
829
|
+
console.error(`${formatError(err)}
|
|
830
|
+
Script completed; only saving the file failed.`);
|
|
831
|
+
if (staged) console.error(`The artifact is still at ${staged}.`);
|
|
832
|
+
process.exit(1);
|
|
833
|
+
}
|
|
834
|
+
}
|
|
815
835
|
} catch (err) {
|
|
816
836
|
console.error(formatError(err));
|
|
817
837
|
process.exit(1);
|
|
@@ -828,29 +848,7 @@ function registerScriptCommands(program2) {
|
|
|
828
848
|
});
|
|
829
849
|
}
|
|
830
850
|
function sleep(ms) {
|
|
831
|
-
return new Promise((
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
// src/commands/profile.ts
|
|
835
|
-
function registerProfileCommands(program2) {
|
|
836
|
-
program2.command("profiles").description("List browser profiles").action(async () => {
|
|
837
|
-
try {
|
|
838
|
-
const result = await api.listProfiles();
|
|
839
|
-
console.log(formatProfiles(result));
|
|
840
|
-
} catch (err) {
|
|
841
|
-
console.error(formatError(err));
|
|
842
|
-
process.exit(1);
|
|
843
|
-
}
|
|
844
|
-
});
|
|
845
|
-
program2.command("delete-profile").description("Delete a named browser profile").argument("<profile-id>", "Profile ID to delete").action(async (profileId) => {
|
|
846
|
-
try {
|
|
847
|
-
const result = await api.deleteProfile(profileId);
|
|
848
|
-
console.log(formatProfileDelete(result.profile_id));
|
|
849
|
-
} catch (err) {
|
|
850
|
-
console.error(formatError(err));
|
|
851
|
-
process.exit(1);
|
|
852
|
-
}
|
|
853
|
-
});
|
|
851
|
+
return new Promise((resolve3) => setTimeout(resolve3, ms));
|
|
854
852
|
}
|
|
855
853
|
|
|
856
854
|
// src/ziniao-doctor.ts
|
|
@@ -999,7 +997,8 @@ function registerZiNiaoCommands(program2) {
|
|
|
999
997
|
console.log(`ZiNiao profiles (${result.profiles.length}):
|
|
1000
998
|
`);
|
|
1001
999
|
for (const p of result.profiles) {
|
|
1002
|
-
|
|
1000
|
+
const site = p.platform_name ? ` [${p.platform_name}]` : "";
|
|
1001
|
+
console.log(` ${p.id} ${p.name}${site}`);
|
|
1003
1002
|
}
|
|
1004
1003
|
console.log("\nUse: browser-cli launch --connector ziniao --ziniao-profile <id> --url <url>");
|
|
1005
1004
|
} catch (err) {
|
|
@@ -1026,15 +1025,33 @@ function registerZiNiaoCommands(program2) {
|
|
|
1026
1025
|
}
|
|
1027
1026
|
|
|
1028
1027
|
// src/commands/recipes.ts
|
|
1029
|
-
import { writeFileSync as
|
|
1028
|
+
import { writeFileSync as writeFileSync4 } from "fs";
|
|
1029
|
+
import { resolve as resolve2 } from "path";
|
|
1030
|
+
async function runExport(script, params, out, timeoutMs) {
|
|
1031
|
+
const outPath = resolve2(out);
|
|
1032
|
+
const result = await api.runRecipeScript(script, { ...params, out_path: outPath }, 2e3, timeoutMs);
|
|
1033
|
+
if (!result.path) {
|
|
1034
|
+
writeFileSync4(outPath, await api.fetchArtifact(result.artifact_path));
|
|
1035
|
+
}
|
|
1036
|
+
console.log(`Saved ${result.filename} -> ${outPath} (${result.size} bytes)`);
|
|
1037
|
+
}
|
|
1030
1038
|
function registerRecipeCommands(program2) {
|
|
1031
|
-
program2.command("export-ad-report").description("Export TikTok Shop last-7-days video ad data into the session workspace").requiredOption("--ziniao-profile <id>", "ZiNiao browser profile ID").option("--out <path>", "Output CSV path", "./tt_video_ad_7d.csv").action(async (opts) => {
|
|
1039
|
+
program2.command("export-ad-report").description("Export TikTok Shop last-7-days video ad data into the session workspace").requiredOption("--ziniao-profile <id>", "ZiNiao browser profile ID").option("--report-url <url>", "Ad dashboard URL (default: TikTok Shop MY local site)").option("--out <path>", "Output CSV path", "./tt_video_ad_7d.csv").action(async (opts) => {
|
|
1032
1040
|
try {
|
|
1033
|
-
const
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
console.
|
|
1041
|
+
const params = { ziniao_profile_id: opts.ziniaoProfile };
|
|
1042
|
+
if (opts.reportUrl) params.report_url = opts.reportUrl;
|
|
1043
|
+
await runExport("tiktok_video_ad_export", params, opts.out, 36e4);
|
|
1044
|
+
} catch (err) {
|
|
1045
|
+
console.error(formatError(err));
|
|
1046
|
+
process.exit(1);
|
|
1047
|
+
}
|
|
1048
|
+
});
|
|
1049
|
+
program2.command("export-order-report").description("Export TikTok Shop order data into the session workspace").requiredOption("--ziniao-profile <id>", "ZiNiao browser profile ID").option("--report-url <url>", "Order list URL (default: TikTok Shop MY local site)").option("--days <n>", "Export the last N days (default: 7, max 365)").option("--out <path>", "Output CSV path", "./tt_orders.csv").action(async (opts) => {
|
|
1050
|
+
try {
|
|
1051
|
+
const params = { ziniao_profile_id: opts.ziniaoProfile };
|
|
1052
|
+
if (opts.reportUrl) params.report_url = opts.reportUrl;
|
|
1053
|
+
if (opts.days) params.days = opts.days;
|
|
1054
|
+
await runExport("tiktok_order_export", params, opts.out, 54e4);
|
|
1038
1055
|
} catch (err) {
|
|
1039
1056
|
console.error(formatError(err));
|
|
1040
1057
|
process.exit(1);
|
|
@@ -1086,7 +1103,6 @@ registerInteractionCommands(program);
|
|
|
1086
1103
|
registerInfoCommands(program);
|
|
1087
1104
|
registerTabCommands(program);
|
|
1088
1105
|
registerScriptCommands(program);
|
|
1089
|
-
registerProfileCommands(program);
|
|
1090
1106
|
registerZiNiaoCommands(program);
|
|
1091
1107
|
registerRecipeCommands(program);
|
|
1092
1108
|
registerBitBrowserCommands(program);
|