@xiaohhhh1/canvas-agent 0.4.12 → 0.4.14

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.
@@ -117,6 +117,7 @@ export function startHttpServer() {
117
117
  app.get("/agent/workflow/downloads", (_req, res) => res.json({ ok: true, ...workflows.downloadState() }));
118
118
  app.post("/agent/workflow/downloads/select-directory", (_req, res) => res.json({ ok: true, selection: workflows.startDownloadDirectorySelection() }));
119
119
  app.get("/agent/workflow/downloads/select-directory/:selectionId", route(async (req, res) => res.json({ ok: true, selection: workflows.downloadDirectorySelection(routeParam(req.params.selectionId)), ...workflows.downloadState() })));
120
+ app.post("/agent/workflow/downloads/set-directory", route(async (req, res) => res.json({ ok: true, ...await workflows.setDownloadDirectory(req.body?.directory) })));
120
121
  app.post("/agent/workflow/downloads/clear-directory", (_req, res) => res.json({ ok: true, ...workflows.clearDownloadDirectory() }));
121
122
  app.post("/agent/workflow/downloads/subscriptions", route(async (req, res) => res.json({ ok: true, subscription: workflows.subscribeDownload(req.body || {}) })));
122
123
  app.post("/agent/workflow/downloads/:batchId/sync", route(async (req, res) => res.json({ ok: true, subscription: await workflows.syncDownload(routeParam(req.params.batchId)) })));
@@ -1,3 +1,4 @@
1
1
  /** Flow C 脚本分段的唯一上限和自动降级顺序,MCP 与调度器必须共用。 */
2
2
  export declare const FLOW_C_SCRIPT_CHUNK_SIZES: readonly [30, 15, 10];
3
3
  export declare const FLOW_C_SCRIPT_CHUNK_MAX: 30;
4
+ export declare const FLOW_C_DOWNLOAD_POLL_MS = 3000;
@@ -1,3 +1,4 @@
1
1
  /** Flow C 脚本分段的唯一上限和自动降级顺序,MCP 与调度器必须共用。 */
2
2
  export const FLOW_C_SCRIPT_CHUNK_SIZES = [30, 15, 10];
3
3
  export const FLOW_C_SCRIPT_CHUNK_MAX = FLOW_C_SCRIPT_CHUNK_SIZES[0];
4
+ export const FLOW_C_DOWNLOAD_POLL_MS = 3_000;
@@ -149,6 +149,19 @@ export declare class WorkflowManager {
149
149
  updatedAt: string;
150
150
  }[];
151
151
  }>;
152
+ setDownloadDirectory(directoryValue: unknown): Promise<{
153
+ configured: boolean;
154
+ directoryName: string | undefined;
155
+ subscriptions: {
156
+ batchId: string;
157
+ status: DownloadStatus;
158
+ downloaded: number;
159
+ market: string | undefined;
160
+ message: string | undefined;
161
+ expiresAt: string | undefined;
162
+ updatedAt: string;
163
+ }[];
164
+ }>;
152
165
  private scheduleScript;
153
166
  /** 同一台电脑串行处理脚本交接,避免多个大批次争用一个 Codex app-server。 */
154
167
  private pumpScriptQueue;
@@ -9,9 +9,8 @@ import { runCodexTurn, startCodexThread } from "../agent/codex.js";
9
9
  import { CONFIG_DIR, ensureSiteWorkspace } from "../config.js";
10
10
  import { logger } from "../utils/logger.js";
11
11
  import { windowsPowerShellExecutable } from "../utils/windows.js";
12
- import { FLOW_C_SCRIPT_CHUNK_MAX, FLOW_C_SCRIPT_CHUNK_SIZES } from "./constants.js";
12
+ import { FLOW_C_DOWNLOAD_POLL_MS, FLOW_C_SCRIPT_CHUNK_MAX, FLOW_C_SCRIPT_CHUNK_SIZES } from "./constants.js";
13
13
  const STATE_FILE = path.join(CONFIG_DIR, "workflow-state.json");
14
- const DOWNLOAD_POLL_MS = 10_000;
15
14
  /** 本机持久化的 Flow C 控制器:脚本交给本机 Codex,视频直接落盘。 */
16
15
  export class WorkflowManager {
17
16
  config;
@@ -29,7 +28,7 @@ export class WorkflowManager {
29
28
  if (record.status === "queued" || record.status === "running")
30
29
  this.scheduleScript(record.id);
31
30
  }
32
- this.downloadTimer = setInterval(() => void this.syncDownloads(), DOWNLOAD_POLL_MS);
31
+ this.downloadTimer = setInterval(() => void this.syncDownloads(), FLOW_C_DOWNLOAD_POLL_MS);
33
32
  this.downloadTimer.unref?.();
34
33
  void this.syncDownloads();
35
34
  }
@@ -154,6 +153,20 @@ export class WorkflowManager {
154
153
  await this.syncDownloads(batchId);
155
154
  return batchId ? publicDownload(this.downloadRecord(batchId)) : this.downloadState();
156
155
  }
156
+ async setDownloadDirectory(directoryValue) {
157
+ const selected = String(directoryValue || "").trim();
158
+ if (!selected || !path.isAbsolute(selected))
159
+ throw new Error("请输入本机文件夹的完整路径");
160
+ const resolved = path.resolve(selected);
161
+ await mkdir(resolved, { recursive: true });
162
+ const info = await stat(resolved);
163
+ if (!info.isDirectory())
164
+ throw new Error("选择的路径不是文件夹");
165
+ this.state.downloadDirectory = resolved;
166
+ this.save();
167
+ void this.syncDownloads();
168
+ return this.downloadState();
169
+ }
157
170
  scheduleScript(_id) {
158
171
  queueMicrotask(() => void this.pumpScriptQueue());
159
172
  }
@@ -183,15 +196,8 @@ export class WorkflowManager {
183
196
  this.directorySelection = { id, status: "cancelled" };
184
197
  return;
185
198
  }
186
- const resolved = path.resolve(selected);
187
- await mkdir(resolved, { recursive: true });
188
- const info = await stat(resolved);
189
- if (!info.isDirectory())
190
- throw new Error("选择的路径不是文件夹");
191
- this.state.downloadDirectory = resolved;
192
- this.save();
193
- this.directorySelection = { id, status: "selected", directoryName: path.basename(resolved) };
194
- void this.syncDownloads();
199
+ const state = await this.setDownloadDirectory(selected);
200
+ this.directorySelection = { id, status: "selected", directoryName: state.directoryName };
195
201
  }
196
202
  catch (error) {
197
203
  this.directorySelection = { id, status: "error", error: error instanceof Error ? error.message : "无法选择本机文件夹" };
@@ -501,15 +507,15 @@ async function isExistingFile(filePath, expectedBytes, expectedSha256 = "") {
501
507
  function selectNativeDirectory() {
502
508
  if (process.platform === "win32") {
503
509
  const script = "$ErrorActionPreference='Stop'; Add-Type -AssemblyName System.Windows.Forms; $d=New-Object System.Windows.Forms.FolderBrowserDialog; $d.Description='选择抖音小辉跨境工具的本机视频保存位置'; $d.ShowNewFolderButton=$true; if($d.ShowDialog() -eq [System.Windows.Forms.DialogResult]::OK){[Console]::OutputEncoding=[Text.Encoding]::UTF8; Write-Output $d.SelectedPath}";
504
- return commandOutput(windowsPowerShellExecutable(), ["-NoProfile", "-STA", "-Command", script]);
510
+ return commandOutput(windowsPowerShellExecutable(), ["-NoProfile", "-STA", "-Command", script], { windowsHide: false });
505
511
  }
506
512
  if (process.platform === "darwin")
507
513
  return commandOutput("osascript", ["-e", "POSIX path of (choose folder with prompt \"选择视频保存位置\")"]);
508
514
  return commandOutput("zenity", ["--file-selection", "--directory", "--title=选择视频保存位置"]);
509
515
  }
510
- function commandOutput(command, args) {
516
+ function commandOutput(command, args, options = {}) {
511
517
  return new Promise((resolve, reject) => {
512
- const child = spawn(command, args, { windowsHide: true, stdio: ["ignore", "pipe", "pipe"] });
518
+ const child = spawn(command, args, { windowsHide: options.windowsHide ?? true, stdio: ["ignore", "pipe", "pipe"] });
513
519
  let stdout = "";
514
520
  let stderr = "";
515
521
  child.stdout.on("data", (chunk) => { stdout += chunk.toString(); });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiaohhhh1/canvas-agent",
3
- "version": "0.4.12",
3
+ "version": "0.4.14",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",