@mkterswingman/5mghost-wonder 0.0.13 → 0.0.15

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.
@@ -0,0 +1,87 @@
1
+ // src/commands/browser.ts
2
+ // Experimental browser runtime commands.
3
+ import { resolveWonderPaths } from "../platform/paths.js";
4
+ import { parseWecomUrl } from "../wecom/url.js";
5
+ import { runBrowserNoExportProbe, runBrowserNoExportRead, } from "../wecom/browser-probe.js";
6
+ export async function runBrowserCommand(argv, context) {
7
+ const [sub, ...rest] = argv;
8
+ switch (sub) {
9
+ case "probe":
10
+ return runBrowserProbe(rest, context);
11
+ case "read":
12
+ return runBrowserRead(rest, context);
13
+ default:
14
+ context.io.stderr(`Unknown subcommand: browser ${sub ?? "(none)"}\n` +
15
+ "Usage: wonder browser probe|read <url> [--headed] [--timeout-ms <ms>] [--save <dir>]");
16
+ return { exitCode: 1 };
17
+ }
18
+ }
19
+ async function runBrowserRead(args, context) {
20
+ return runBrowserAction(args, context, "read");
21
+ }
22
+ async function runBrowserProbe(args, context) {
23
+ return runBrowserAction(args, context, "probe");
24
+ }
25
+ async function runBrowserAction(args, context, action) {
26
+ let url;
27
+ let saveDir;
28
+ let timeoutMs;
29
+ let headed = false;
30
+ for (let i = 0; i < args.length; i++) {
31
+ const arg = args[i];
32
+ if (arg === "--save" && args[i + 1]) {
33
+ saveDir = args[++i];
34
+ }
35
+ else if (arg === "--timeout-ms" && args[i + 1]) {
36
+ timeoutMs = Number(args[++i]);
37
+ }
38
+ else if (arg === "--headed") {
39
+ headed = true;
40
+ }
41
+ else if (!arg.startsWith("-")) {
42
+ url = arg;
43
+ }
44
+ }
45
+ if (!url) {
46
+ context.io.stderr(JSON.stringify({
47
+ error: "missing_url",
48
+ message: `用法:wonder browser ${action} <url> [--headed] [--timeout-ms <ms>] [--save <dir>]`,
49
+ }));
50
+ return { exitCode: 1, telemetry: { outcome: "failure", errorKind: "missing_url" } };
51
+ }
52
+ const parsed = parseWecomUrl(url);
53
+ if (!parsed.ok) {
54
+ context.io.stderr(JSON.stringify({
55
+ error: "invalid_url",
56
+ message: "无法识别的 WeCom URL",
57
+ }));
58
+ return { exitCode: 1, telemetry: { outcome: "failure", errorKind: "invalid_url" } };
59
+ }
60
+ if (timeoutMs !== undefined && (!Number.isFinite(timeoutMs) || timeoutMs < 1000 || timeoutMs > 60_000)) {
61
+ context.io.stderr(JSON.stringify({
62
+ error: "invalid_timeout",
63
+ message: "--timeout-ms must be between 1000 and 60000",
64
+ }));
65
+ return { exitCode: 1, telemetry: { outcome: "failure", errorKind: "invalid_timeout" } };
66
+ }
67
+ const paths = resolveWonderPaths({ homeDir: context.homeDir });
68
+ const options = {
69
+ url,
70
+ chromeProfilePath: paths.chromeProfilePath,
71
+ saveDir,
72
+ headed,
73
+ timeoutMs,
74
+ io: context.io,
75
+ };
76
+ const result = action === "read"
77
+ ? await runBrowserNoExportRead(options)
78
+ : await runBrowserNoExportProbe(options);
79
+ context.io.stdout(JSON.stringify(result));
80
+ return {
81
+ exitCode: result.status === "fail" ? 1 : 0,
82
+ telemetry: {
83
+ outcome: result.status === "fail" ? "failure" : "success",
84
+ errorKind: result.status === "fail" ? "browser_probe_failed" : undefined,
85
+ },
86
+ };
87
+ }
@@ -30,6 +30,12 @@ export function renderHelpText() {
30
30
  " doc/slide → download file + path JSON",
31
31
  " read <url> --tab <name> Read specific xlsx tab (cells + merges + images)",
32
32
  " read <url> --save <dir> Output directory (default: ~/Downloads/5mghost-wonder/)",
33
+ "",
34
+ "Experimental browser runtime:",
35
+ " browser probe <url> Open in Wonder browser profile and capture",
36
+ " no-export evidence summaries",
37
+ " browser read <url> Experimental no-export text read",
38
+ " browser probe <url> --headed Show the browser while probing",
33
39
  ].join("\n");
34
40
  }
35
41
  export async function runHelpCommand(io) {
@@ -11,6 +11,7 @@ import { runSetupCommand } from "./setup.js";
11
11
  import { runAuthCommand } from "./auth.js";
12
12
  import { runWecom } from "./wecom.js";
13
13
  import { runReadCommand } from "./read.js";
14
+ import { runBrowserCommand } from "./browser.js";
14
15
  export async function dispatchWonderCommand(argv, context) {
15
16
  const [cmd, ...rest] = argv;
16
17
  switch (cmd) {
@@ -30,6 +31,8 @@ export async function dispatchWonderCommand(argv, context) {
30
31
  return runWecom(rest, context);
31
32
  case "read":
32
33
  return runRead(rest, context);
34
+ case "browser":
35
+ return runBrowserCommand(rest, context);
33
36
  case "--help":
34
37
  case "-h":
35
38
  case "help":
@@ -2,7 +2,7 @@
2
2
  // Factory for the wonder telemetry runtime.
3
3
  // Returns null on any construction error — telemetry must never crash the CLI.
4
4
  import { DEFAULT_AUTH_URL, TokenManager } from "@mkterswingman/5mghost-auth";
5
- import { TelemetryRuntime, TelemetrySender } from "@mkterswingman/5mghost-telemetry";
5
+ import { TelemetryRuntime, TelemetrySender, } from "@mkterswingman/5mghost-telemetry";
6
6
  import packageJson from "../../package.json" with { type: "json" };
7
7
  export function createWonderTelemetryRuntime(options = {}) {
8
8
  try {
@@ -17,11 +17,12 @@ export function createWonderTelemetryRuntime(options = {}) {
17
17
  authUrl,
18
18
  homeDir: options.homeDir,
19
19
  });
20
+ const sender = new SafeTelemetrySender(new TelemetrySender(tokenManager, ingestUrl));
20
21
  return new TelemetryRuntime({
21
22
  product: "5mghost-wonder",
22
23
  productVersion: packageJson.version,
23
24
  homeDir: options.homeDir,
24
- sender: new TelemetrySender(tokenManager, ingestUrl),
25
+ sender,
25
26
  });
26
27
  }
27
28
  catch {
@@ -29,3 +30,23 @@ export function createWonderTelemetryRuntime(options = {}) {
29
30
  return null;
30
31
  }
31
32
  }
33
+ class SafeTelemetrySender {
34
+ delegate;
35
+ constructor(delegate) {
36
+ this.delegate = delegate;
37
+ }
38
+ async sendBatch(events) {
39
+ try {
40
+ return await this.delegate.sendBatch(events);
41
+ }
42
+ catch (err) {
43
+ const errorName = err instanceof Error ? err.name : typeof err;
44
+ return {
45
+ disposition: {
46
+ retryable: true,
47
+ reason: `transport:${errorName}`,
48
+ },
49
+ };
50
+ }
51
+ }
52
+ }