@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.
package/dist/server/http.js
CHANGED
|
@@ -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)) })));
|
|
@@ -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;
|
package/dist/workflow/manager.js
CHANGED
|
@@ -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(),
|
|
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
|
|
187
|
-
|
|
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(); });
|