tickflow-assist 0.2.2 → 0.2.4

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.
@@ -319,13 +319,14 @@ async function discoverConfiguredChannels(configPath) {
319
319
  }
320
320
  async function promptSelect(rl, label, choices, defaultValue) {
321
321
  const defaultIndex = Math.max(0, choices.findIndex((c) => c.value === defaultValue));
322
- console.log(` ${label}`);
322
+ let promptText = `\n ${label}\n`;
323
323
  for (let i = 0; i < choices.length; i++) {
324
324
  const marker = i === defaultIndex ? " (默认)" : "";
325
- console.log(` ${i + 1}) ${choices[i].label}${marker}`);
325
+ promptText += ` ${i + 1}) ${choices[i].label}${marker}\n`;
326
326
  }
327
+ promptText += ` 请选择 (1-${choices.length}) [${defaultIndex + 1}]: `;
327
328
  while (true) {
328
- const answer = (await rl.question(` 请选择 (1-${choices.length}) [${defaultIndex + 1}]: `)).trim();
329
+ const answer = (await rl.question(promptText)).trim();
329
330
  if (!answer) {
330
331
  return choices[defaultIndex].value;
331
332
  }
@@ -340,13 +341,12 @@ async function promptAlertChannel(rl, configPath, defaultChannel) {
340
341
  const configured = await discoverConfiguredChannels(configPath);
341
342
  let selectedChannel = defaultChannel;
342
343
  if (configured.length > 0) {
343
- console.log(" 检测到 openclaw.json 中已有通道配置:");
344
344
  const choices = configured.map((c) => {
345
345
  const acctLabel = c.accounts.length > 0 ? ` (accounts: ${c.accounts.join(", ")})` : "";
346
346
  return { value: c.channel, label: `${c.channel}${acctLabel}` };
347
347
  });
348
348
  choices.push({ value: "__manual__", label: "手动输入其他通道" });
349
- selectedChannel = await promptSelect(rl, "推送通道", choices, defaultChannel);
349
+ selectedChannel = await promptSelect(rl, "检测到 openclaw.json 中已有配置,请选择推送通道", choices, defaultChannel);
350
350
  if (selectedChannel === "__manual__") {
351
351
  selectedChannel = await promptString(rl, "Alert Channel", defaultChannel, true);
352
352
  }
@@ -354,6 +354,7 @@ async function promptAlertChannel(rl, configPath, defaultChannel) {
354
354
  else {
355
355
  const knownChannels = [
356
356
  { value: "telegram", label: "telegram" },
357
+ { value: "discord", label: "discord" },
357
358
  { value: "qqbot", label: "qqbot" },
358
359
  { value: "wecom", label: "wecom" },
359
360
  ];
@@ -434,11 +435,14 @@ async function promptForConfig(options, existing, pluginDir, configPath) {
434
435
  const alertResult = await promptAlertChannel(rl, configPath, seed.alertChannel);
435
436
  seed.alertChannel = alertResult.channel;
436
437
  seed.alertAccount = alertResult.account;
437
- console.log(` 已选择通道: ${seed.alertChannel}`);
438
+ let targetLabel = "Alert Target";
438
439
  if (seed.alertAccount) {
439
- console.log(` 已选择账号: ${seed.alertAccount}`);
440
+ targetLabel = `已选通道 [${seed.alertChannel}] 及账号 [${seed.alertAccount}],请输入 Alert Target`;
440
441
  }
441
- seed.alertTarget = await promptString(rl, "Alert Target", seed.alertTarget, true);
442
+ else {
443
+ targetLabel = `已选通道 [${seed.alertChannel}],请输入 Alert Target`;
444
+ }
445
+ seed.alertTarget = await promptString(rl, targetLabel, seed.alertTarget, true);
442
446
  seed.requestInterval = await promptInteger(rl, "Request Interval (seconds)", seed.requestInterval, 5);
443
447
  seed.dailyUpdateNotify = await promptBoolean(rl, "Daily Update Notify", seed.dailyUpdateNotify);
444
448
  }
@@ -589,17 +593,59 @@ function runOpenClaw(bin, args, description) {
589
593
  console.warn(`Warning: ${description} exited with status ${result.status}`);
590
594
  }
591
595
  }
592
- function setupPythonDeps(pythonWorkdir) {
596
+ async function setupPythonDeps(pythonWorkdir, nonInteractive) {
593
597
  let uvBin = "uv";
594
598
  try {
595
599
  const which = spawnSync("which", ["uv"], { encoding: "utf-8" });
596
600
  if (which.status !== 0) {
597
- console.warn("Warning: uv not found in PATH, skipping Python dependency setup.");
598
- console.warn("Please install uv (https://docs.astral.sh/uv/) and run 'uv sync' manually in:");
599
- console.warn(` ${pythonWorkdir}`);
600
- return;
601
+ console.log("\n ⚠️ 找不到 uv (Python 包管理工具)。");
602
+ let shouldInstall = false;
603
+ if (!nonInteractive) {
604
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
605
+ const answer = (await rl.question(" 是否自动下载并安装 uv?(y/n) [y]: ")).trim().toLowerCase();
606
+ rl.close();
607
+ if (!answer || ["y", "yes", "1", "true"].includes(answer)) {
608
+ shouldInstall = true;
609
+ }
610
+ }
611
+ if (shouldInstall) {
612
+ console.log(" 正在安装 uv...");
613
+ const installResult = spawnSync("sh", ["-c", "curl -LsSf https://astral.sh/uv/install.sh | sh"], { stdio: "inherit" });
614
+ if (installResult.status !== 0) {
615
+ console.warn(" uv 安装失败,跳过 Python 依赖安装。");
616
+ return;
617
+ }
618
+ let foundUv = false;
619
+ let installedLoc = "";
620
+ for (const loc of [path.join(os.homedir(), ".local", "bin", "uv"), path.join(os.homedir(), ".cargo", "bin", "uv")]) {
621
+ try {
622
+ await access(loc);
623
+ uvBin = loc;
624
+ installedLoc = loc;
625
+ foundUv = true;
626
+ break;
627
+ }
628
+ catch {
629
+ // ignore
630
+ }
631
+ }
632
+ if (!foundUv) {
633
+ uvBin = "uv";
634
+ }
635
+ else {
636
+ console.log(`\n ✅ uv 已自动安装到 ${installedLoc}`);
637
+ console.log(" ⚠️ 温馨提示:为了在终端能直接使用 uv 命令,您可能需要执行 `source $HOME/.local/bin/env` \n 或自行将其所在目录添加入系统的 PATH 环境变量中。\n");
638
+ }
639
+ }
640
+ else {
641
+ console.warn("\n ⚠️ 跳过 Python 依赖安装。请手动安装 uv (https://docs.astral.sh/uv/) 并执行 'uv sync',路径:");
642
+ console.warn(` ${pythonWorkdir}`);
643
+ return;
644
+ }
645
+ }
646
+ else {
647
+ uvBin = which.stdout.trim() || "uv";
601
648
  }
602
- uvBin = which.stdout.trim() || "uv";
603
649
  }
604
650
  catch {
605
651
  // fall through with default "uv"
@@ -631,7 +677,7 @@ async function configureOpenClaw(options) {
631
677
  await ensurePathNotice(config.calendarFile, "calendarFile");
632
678
  await ensurePathNotice(config.pythonWorkdir, "pythonWorkdir");
633
679
  if (options.pythonSetup) {
634
- setupPythonDeps(config.pythonWorkdir);
680
+ await setupPythonDeps(config.pythonWorkdir, options.nonInteractive);
635
681
  }
636
682
  applyPluginConfig(root, config, target);
637
683
  const backupPath = await writeConfig(configPath, root);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "tickflow-assist",
3
3
  "name": "TickFlow Assist",
4
- "version": "0.2.2",
4
+ "version": "0.2.4",
5
5
  "description": "A-share watchlist analysis, monitoring, and alert delivery powered by TickFlow and OpenClaw.",
6
6
  "skills": [
7
7
  "skills"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tickflow-assist",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "OpenClaw plugin for TickFlow-based A-share analysis, monitoring, and alerting.",
5
5
  "license": "MIT",
6
6
  "type": "module",