triflux 10.9.23 → 10.9.24

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.
@@ -30,5 +30,5 @@
30
30
  ]
31
31
  }
32
32
  ],
33
- "version": "10.9.21"
33
+ "version": "10.9.24"
34
34
  }
@@ -557,6 +557,7 @@
557
557
  },
558
558
  {
559
559
  "id": "gstack-ship",
560
+ "disabled": true,
560
561
  "patterns": [
561
562
  {
562
563
  "source": "(?:배포해|PR\\s*만들|릴리스\\s*해|머지하고\\s*배포)",
@@ -2,7 +2,8 @@
2
2
  // ADR-006: --output-format stream-json 기반 단발 실행 워커.
3
3
 
4
4
  import { spawn } from "node:child_process";
5
- import { isAbsolute } from "node:path";
5
+ import { existsSync } from "node:fs";
6
+ import { extname } from "node:path";
6
7
  import readline from "node:readline";
7
8
 
8
9
  import { extractText, terminateChild, withRetry } from "./worker-utils.mjs";
@@ -15,6 +16,51 @@ function toStringList(value) {
15
16
  return value.map((item) => String(item ?? "").trim()).filter(Boolean);
16
17
  }
17
18
 
19
+ /**
20
+ * Windows에서 cmd.exe에 전달할 인자를 안전하게 quoting한다.
21
+ * 빈 문자열은 ""로, 특수문자 포함 시 큰따옴표로 감싼다.
22
+ */
23
+ function quoteWindowsCmdArg(value) {
24
+ const raw = String(value ?? "");
25
+ if (raw.length === 0) return '""';
26
+ if (!/[\s"()^&|<>%!]/.test(raw)) return raw;
27
+ const escaped = raw
28
+ .replace(/%/g, "%%")
29
+ .replace(/(\\*)"/g, '$1$1\\"')
30
+ .replace(/(\\+)$/g, "$1$1");
31
+ return `"${escaped}"`;
32
+ }
33
+
34
+ /**
35
+ * Windows npm shim(.cmd) spawn 문제를 해결한다.
36
+ * - command -v가 확장자 없는 경로를 반환하면 .exe → .cmd → .bat 순으로 탐색
37
+ * - .cmd/.bat는 CVE-2024-27980 이후 shell:false에서 실행 불가하므로 cmd.exe /d /s /c 경유
38
+ * - .exe는 직접 실행
39
+ * - non-Windows는 그대로 통과
40
+ */
41
+ function buildSpawnSpec(command, args) {
42
+ if (process.platform !== "win32") {
43
+ return { command, args, shell: false };
44
+ }
45
+
46
+ let resolved = command;
47
+ if (!extname(resolved)) {
48
+ for (const ext of [".exe", ".cmd", ".bat"]) {
49
+ if (existsSync(resolved + ext)) {
50
+ resolved = resolved + ext;
51
+ break;
52
+ }
53
+ }
54
+ }
55
+
56
+ if (/\.(cmd|bat)$/i.test(resolved)) {
57
+ const line = [resolved, ...args].map(quoteWindowsCmdArg).join(" ");
58
+ return { command: "cmd.exe", args: ["/d", "/s", "/c", line], shell: false };
59
+ }
60
+
61
+ return { command: resolved, args, shell: false };
62
+ }
63
+
18
64
  function safeJsonParse(line) {
19
65
  try {
20
66
  return JSON.parse(line);
@@ -223,12 +269,17 @@ export class GeminiWorker {
223
269
  }),
224
270
  ];
225
271
 
226
- const child = spawn(this.command, args, {
272
+ const {
273
+ command: spawnCmd,
274
+ args: spawnArgs,
275
+ shell,
276
+ } = buildSpawnSpec(this.command, args);
277
+ const child = spawn(spawnCmd, spawnArgs, {
227
278
  cwd: options.cwd || this.cwd,
228
279
  env: { ...this.env, ...(options.env || {}) },
229
280
  stdio: ["pipe", "pipe", "pipe"],
230
281
  windowsHide: true,
231
- shell: process.platform === "win32" && !isAbsolute(this.command),
282
+ shell,
232
283
  });
233
284
 
234
285
  this.child = child;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "triflux",
3
- "version": "10.9.23",
3
+ "version": "10.9.24",
4
4
  "description": "CLI-first multi-model orchestrator for Claude Code — route tasks to Codex, Gemini, and Claude",
5
5
  "type": "module",
6
6
  "bin": {
@@ -7,6 +7,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
7
7
 
8
8
  const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
9
9
  const FACTORY_CANDIDATES = [
10
+ resolve(process.cwd(), "hub/workers/factory.mjs"),
10
11
  resolve(SCRIPT_DIR, "../hub/workers/factory.mjs"),
11
12
  resolve(SCRIPT_DIR, "./hub/workers/factory.mjs"),
12
13
  ];