@lalalic/markcut 3.2.13 → 3.2.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/README.md +10 -0
- package/package.json +1 -1
- package/src/queue-cli.mjs +17 -0
- package/src/render/cli-tools.ts +6 -5
- package/src/vision/candidate.mjs +2 -1
- package/src/vision/cli.mjs +2 -1
- package/src/vision/default-infer.mjs +3 -1
- package/tests/queue-cli.test.ts +17 -0
package/README.md
CHANGED
|
@@ -370,3 +370,13 @@ export MARKCUT_VTT_CLI='my-video-backend --prompt "{prompt}" {input}'
|
|
|
370
370
|
```
|
|
371
371
|
|
|
372
372
|
## Architecture
|
|
373
|
+
|
|
374
|
+
### Global queue CLI template (opt-in)
|
|
375
|
+
|
|
376
|
+
Set `MARKCUT_QUEUE_CLI_TEMPLATE` in Markcut's environment to route generation and vision CLI subprocesses through the PM2-managed queue worker. `{command}` is the shell-quoted original command; the queue wrapper sets `MARKCUT_QUEUE_ACTIVE=1` for descendants to prevent re-entrance.
|
|
377
|
+
|
|
378
|
+
```bash
|
|
379
|
+
export MARKCUT_QUEUE_CLI_TEMPLATE='node "$HOME/.agents/skills/queue/queue.mjs" run --queue media-ai -- /bin/sh -c {command}'
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
The template must use an executable reachable from the process environment. To avoid ambiguity with shell quoting of `$HOME`, use a literal absolute path in production. If unset, execution is unchanged. Rendering cache continues to be managed by Markcut, not the queue. No service restart or Markcut-specific queue daemon is required.
|
package/package.json
CHANGED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** Optional global queue CLI template for expensive Markcut operations. */
|
|
2
|
+
export function shellQuote(value) {
|
|
3
|
+
return "'" + String(value).replace(/'/g, "'\\''") + "'";
|
|
4
|
+
}
|
|
5
|
+
export function queueCommand(command, env = process.env) {
|
|
6
|
+
const template = env.MARKCUT_QUEUE_CLI_TEMPLATE;
|
|
7
|
+
if (!template || env.MARKCUT_QUEUE_ACTIVE === "1") return command;
|
|
8
|
+
if (!template.includes("{command}")) throw Error("MARKCUT_QUEUE_CLI_TEMPLATE must contain {command}");
|
|
9
|
+
// The worker runs a shell to execute the original CLI without re-entering the queue.
|
|
10
|
+
return template.replaceAll("{command}", shellQuote(`MARKCUT_QUEUE_ACTIVE=1 ${command}`));
|
|
11
|
+
}
|
|
12
|
+
export function queueArgv(command, argv, env = process.env) {
|
|
13
|
+
const template = env.MARKCUT_QUEUE_CLI_TEMPLATE;
|
|
14
|
+
if (!template || env.MARKCUT_QUEUE_ACTIVE === "1") return { command, argv };
|
|
15
|
+
const cmd = [command, ...argv].map(shellQuote).join(" ");
|
|
16
|
+
return { command: "/bin/sh", argv: ["-c", queueCommand(cmd, env)] };
|
|
17
|
+
}
|
package/src/render/cli-tools.ts
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* Override via MARKCUT_TTS_CLI / MARKCUT_STT_CLI / MARKCUT_TTI_CLI / MARKCUT_TTV_CLI.
|
|
12
12
|
*/
|
|
13
13
|
import { execSync, exec } from "node:child_process";
|
|
14
|
+
import { queueCommand } from "../queue-cli.mjs";
|
|
14
15
|
import { existsSync, mkdirSync } from "node:fs";
|
|
15
16
|
import { dirname } from "node:path";
|
|
16
17
|
import {
|
|
@@ -89,7 +90,7 @@ export function generateTTS(text: string, outputPath: string, cli?: string): str
|
|
|
89
90
|
mkdirSync(dirname(outputPath), { recursive: true });
|
|
90
91
|
const cmd = substituteCli(cli ?? DEFAULT_TTS_CLI, text, outputPath);
|
|
91
92
|
try {
|
|
92
|
-
execSync(cmd, { stdio: "pipe" });
|
|
93
|
+
execSync(queueCommand(cmd), { stdio: "pipe" });
|
|
93
94
|
} catch (e: any) {
|
|
94
95
|
console.warn(` ⚠ TTS failed: ${e.message}`);
|
|
95
96
|
return "";
|
|
@@ -104,7 +105,7 @@ export function generateTTS(text: string, outputPath: string, cli?: string): str
|
|
|
104
105
|
export async function generateSTT(audioPath: string, outputDir: string, cli?: string): Promise<void> {
|
|
105
106
|
const cmd = substituteCli(cli ?? DEFAULT_STT_CLI, audioPath, outputDir);
|
|
106
107
|
try {
|
|
107
|
-
execSync(cmd, { stdio: "pipe" });
|
|
108
|
+
execSync(queueCommand(cmd), { stdio: "pipe" });
|
|
108
109
|
} catch (e: any) {
|
|
109
110
|
console.warn(` ⚠ STT failed: ${e.message}`);
|
|
110
111
|
}
|
|
@@ -119,7 +120,7 @@ export function generateTTI(prompt: string, outputPath: string, cli?: string, se
|
|
|
119
120
|
mkdirSync(dirname(outputPath), { recursive: true });
|
|
120
121
|
const cmd = substituteCli(cli ?? DEFAULT_TTI_CLI, prompt, outputPath, seed);
|
|
121
122
|
try {
|
|
122
|
-
execSync(cmd, {
|
|
123
|
+
execSync(queueCommand(cmd), {
|
|
123
124
|
encoding: "utf-8",
|
|
124
125
|
stdio: ["pipe", "pipe", "pipe"],
|
|
125
126
|
timeout: 300_000,
|
|
@@ -162,7 +163,7 @@ export function generateTTV(
|
|
|
162
163
|
}
|
|
163
164
|
try {
|
|
164
165
|
execSync(
|
|
165
|
-
`ffmpeg -y -loop 1 -i "${pngPath}" -c:v libx264 -t 3 -pix_fmt yuv420p "${outputPath}"
|
|
166
|
+
queueCommand(`ffmpeg -y -loop 1 -i "${pngPath}" -c:v libx264 -t 3 -pix_fmt yuv420p "${outputPath}"`),
|
|
166
167
|
{ stdio: ["pipe", "pipe", "pipe"], timeout: 60_000 },
|
|
167
168
|
);
|
|
168
169
|
} catch (e: any) {
|
|
@@ -177,7 +178,7 @@ export function generateTTV(
|
|
|
177
178
|
// Custom CLI mode: substitute template and run
|
|
178
179
|
const cmd = substituteCli(cli, prompt, outputPath, seed);
|
|
179
180
|
try {
|
|
180
|
-
execSync(cmd, {
|
|
181
|
+
execSync(queueCommand(cmd), {
|
|
181
182
|
encoding: "utf-8",
|
|
182
183
|
stdio: ["pipe", "pipe", "pipe"],
|
|
183
184
|
timeout: 300_000,
|
package/src/vision/candidate.mjs
CHANGED
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
import { basename, dirname, extname, join, resolve } from "node:path";
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
import { execSync } from "node:child_process";
|
|
8
|
+
import { queueCommand } from "../queue-cli.mjs";
|
|
8
9
|
import { VIDEO_EXTS, DEFAULT_VTT_SAMPLE_INTERVAL } from "../config.mjs";
|
|
9
10
|
|
|
10
11
|
export const CANDIDATE_CONTRACT_VERSION = 1;
|
|
@@ -107,7 +108,7 @@ function shQuote(value) {
|
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
function run(command, options = {}) {
|
|
110
|
-
return execSync(command, {
|
|
111
|
+
return execSync(queueCommand(command), {
|
|
111
112
|
encoding: "utf-8",
|
|
112
113
|
stdio: ["pipe", "pipe", "pipe"],
|
|
113
114
|
timeout: 300_000,
|
package/src/vision/cli.mjs
CHANGED
|
@@ -37,6 +37,7 @@
|
|
|
37
37
|
*/
|
|
38
38
|
|
|
39
39
|
import { execSync, spawn } from "node:child_process";
|
|
40
|
+
import { queueCommand } from "../queue-cli.mjs";
|
|
40
41
|
import { createHash } from "node:crypto";
|
|
41
42
|
import {
|
|
42
43
|
existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, statSync, renameSync, rmSync,
|
|
@@ -67,7 +68,7 @@ function emitWarn(msg) { console.error(`⚠️ ${msg}`); }
|
|
|
67
68
|
function emitError(msg) { console.error(`❌ ${msg}`); }
|
|
68
69
|
|
|
69
70
|
function run(cmd, opts = {}) {
|
|
70
|
-
return execSync(cmd, {
|
|
71
|
+
return execSync(queueCommand(cmd), {
|
|
71
72
|
encoding: "utf-8",
|
|
72
73
|
stdio: ["pipe", "pipe", "pipe"],
|
|
73
74
|
timeout: 300_000,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { spawnSync } from "node:child_process";
|
|
3
|
+
import { queueArgv } from "../queue-cli.mjs";
|
|
3
4
|
import { accessSync, constants, existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
4
5
|
import { tmpdir } from "node:os";
|
|
5
6
|
import { join } from "node:path";
|
|
@@ -28,7 +29,8 @@ function which(name) {
|
|
|
28
29
|
}
|
|
29
30
|
|
|
30
31
|
function run(command, args, options = {}) {
|
|
31
|
-
const
|
|
32
|
+
const queued = queueArgv(command, args);
|
|
33
|
+
const r = spawnSync(queued.command, queued.argv, { encoding: "utf8", timeout: options.timeout || 600_000, env: process.env });
|
|
32
34
|
return { ok: r.status === 0, stdout: (r.stdout || "").trim(), stderr: (r.stderr || "").trim(), status: r.status };
|
|
33
35
|
}
|
|
34
36
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { queueCommand, queueArgv } from "../src/queue-cli.mjs";
|
|
3
|
+
describe("global queue CLI template", () => {
|
|
4
|
+
it("is disabled unless global env is set", () => expect(queueCommand("echo one", {})).toBe("echo one"));
|
|
5
|
+
it("wraps a shell command with a recursion guard", () => {
|
|
6
|
+
const env = { MARKCUT_QUEUE_CLI_TEMPLATE: "node queue.mjs run --queue media-ai -- /bin/sh -c {command}" };
|
|
7
|
+
const result = queueCommand('echo "a b"', env);
|
|
8
|
+
expect(result).toContain("MARKCUT_QUEUE_ACTIVE=1");
|
|
9
|
+
expect(result).toContain("node queue.mjs run --queue media-ai");
|
|
10
|
+
expect(queueCommand("echo no nested", {...env, MARKCUT_QUEUE_ACTIVE:"1"})).toBe("echo no nested");
|
|
11
|
+
});
|
|
12
|
+
it("rejects unusable templates", () => expect(() => queueCommand("echo 1", {MARKCUT_QUEUE_CLI_TEMPLATE:"invalid"})).toThrow(/\{command\}/));
|
|
13
|
+
it("handles spawned CLI argv without escaping loss", () => {
|
|
14
|
+
const r=queueArgv("python",["file with spaces", "quoted'word"],{MARKCUT_QUEUE_CLI_TEMPLATE:"queue run -- /bin/sh -c {command}"});
|
|
15
|
+
expect(r.command).toBe("/bin/sh");expect(r.argv[1]).toContain("MARKCUT_QUEUE_ACTIVE");
|
|
16
|
+
});
|
|
17
|
+
});
|