@kw-yeh/vibeflow 4.7.1 → 4.8.0
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/vibeflow.mjs +685 -345
- package/dist/web/404/index.html +1 -1
- package/dist/web/404.html +1 -1
- package/dist/web/_next/static/{rD1tZjqo_1oM7qAU7uvic → MCpYTSYna3Ll-2lXwbETR}/_buildManifest.js +1 -1
- package/dist/web/_next/static/chunks/pages/home-ead2bccf81dc3d85.js +12 -0
- package/dist/web/_next/static/css/{d25415d1bec170b4.css → 0e63f58e447f85ed.css} +1 -1
- package/dist/web/home/index.html +1 -1
- package/package.json +1 -1
- package/dist/web/_next/static/chunks/pages/home-78adf6369b572108.js +0 -12
- /package/dist/web/_next/static/{rD1tZjqo_1oM7qAU7uvic → MCpYTSYna3Ll-2lXwbETR}/_ssgManifest.js +0 -0
package/dist/vibeflow.mjs
CHANGED
|
@@ -499,6 +499,196 @@ var init_tmux_backend = __esm({
|
|
|
499
499
|
}
|
|
500
500
|
});
|
|
501
501
|
|
|
502
|
+
// packages/core/src/launch.ts
|
|
503
|
+
function pathBasename(p) {
|
|
504
|
+
const norm = p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
505
|
+
const i = norm.lastIndexOf("/");
|
|
506
|
+
return i >= 0 ? norm.slice(i + 1) : norm;
|
|
507
|
+
}
|
|
508
|
+
function agentArtifactsDir(worktreePath, workspacePath) {
|
|
509
|
+
if (!worktreePath || !workspacePath) return null;
|
|
510
|
+
const dir = toShellPath(workspacePath);
|
|
511
|
+
const ws = pathBasename(worktreePath);
|
|
512
|
+
return `${dir}/${ws}${ARTIFACTS_DIR_SUFFIX}`;
|
|
513
|
+
}
|
|
514
|
+
function buildArtifactPrompt(artifactsDir = ARTIFACTS_FALLBACK_DIR) {
|
|
515
|
+
return [
|
|
516
|
+
"Artifact \u8A2D\u5B9A\uFF08\u672C\u6B21 session \u5168\u7A0B\u9069\u7528\uFF09\uFF1A",
|
|
517
|
+
`1. \u672C\u4EFB\u52D9\u7684 Artifact \u8CC7\u6599\u593E\u662F ${artifactsDir}/\uFF1B\u9700\u8981\u4FDD\u5B58\u7D66\u4F7F\u7528\u8005\u7684\u9A57\u8B49\u8B49\u64DA\u6216\u5831\u544A\u6642\uFF0C\u8ACB\u5BEB\u5165\u6B64\u8CC7\u6599\u593E\uFF08\u4E0D\u5B58\u5728\u8ACB\u5EFA\u7ACB\uFF09\u3002`,
|
|
518
|
+
`2. \u4F7F\u7528\u8005\u6703\u5728 VibeFlow \u7684 Artifacts \u5206\u9801\u6AA2\u8996\u6839\u76EE\u9304\u5167\u5BB9\uFF1B\u53EA\u653E\u4F60\u6703\u4E3B\u52D5\u8ACB\u4F7F\u7528\u8005\u904E\u76EE\u7684\u6A94\u6848\u3002`,
|
|
519
|
+
`3. \u4F60\u81EA\u5DF1\u7684\u5DE5\u4F5C\u66AB\u5B58\uFF08\u4E00\u6B21\u6027 script\u3001log\u3001\u4E2D\u9593\u8F38\u51FA\u3001debug \u6A94\uFF09\u653E\u5728 ${artifactsDir}/${SCRATCH_DIR_NAME}/\u3002`,
|
|
520
|
+
`4. \u82E5\u5DE5\u5177\u5148\u8F38\u51FA\u5230 /tmp\u3001/private/tmp\u3001$TMPDIR \u6216 ~/Downloads\uFF0C\u8ACB\u628A\u6700\u7D42\u8981\u5448\u73FE\u7684\u6A94\u6848\u8907\u88FD\u5230 ${artifactsDir}/ \u4E26\u78BA\u8A8D\u5B58\u5728\u3002`,
|
|
521
|
+
"5. Artifact \u8CC7\u6599\u593E\u4F4D\u65BC worktree \u4E4B\u5916\uFF1B\u4E0D\u8981\u628A\u5176\u4E2D\u5167\u5BB9\u52A0\u5165 git commit\uFF0C\u4E5F\u4E0D\u8981\u628A\u6700\u7D42\u4EA4\u4ED8\u7269\u53EA\u7559\u5728\u9019\u88E1\u3002"
|
|
522
|
+
].join("\n");
|
|
523
|
+
}
|
|
524
|
+
function claudePermissionFlag(autoMode) {
|
|
525
|
+
return autoMode ? ` --permission-mode ${AUTO_PERMISSION_MODE}` : "";
|
|
526
|
+
}
|
|
527
|
+
function shellQuote(s) {
|
|
528
|
+
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
529
|
+
}
|
|
530
|
+
function toShellPath(p) {
|
|
531
|
+
return p.replace(/\\/g, "/");
|
|
532
|
+
}
|
|
533
|
+
function boardEnvPrefix(task, opts) {
|
|
534
|
+
const board = opts?.boardCli;
|
|
535
|
+
if (!board) return "";
|
|
536
|
+
const entries = [
|
|
537
|
+
["VIBEFLOW_TASK_ID", task.id],
|
|
538
|
+
["VIBEFLOW_PROJECT_PATH", task.projectPath],
|
|
539
|
+
["VIBEFLOW_BRANCH", task.branch],
|
|
540
|
+
["VIBEFLOW_BASE_BRANCH", task.baseBranch],
|
|
541
|
+
["VIBEFLOW_STORE_DIR", board?.storeDir],
|
|
542
|
+
["VIBEFLOW_CLI", board?.cliPath],
|
|
543
|
+
["VIBEFLOW_CLI_LOADER", board?.loaderPath],
|
|
544
|
+
["VIBEFLOW_AUTO_MODE", opts?.autoMode ? "1" : "0"]
|
|
545
|
+
];
|
|
546
|
+
const exports = entries.filter((entry) => Boolean(entry[1])).map(([key, value]) => `${key}=${shellQuote(toShellPath(value))}`);
|
|
547
|
+
return exports.length ? `export ${exports.join(" ")}; ` : "";
|
|
548
|
+
}
|
|
549
|
+
function codexAutoFlag(autoMode) {
|
|
550
|
+
return autoMode ? "--dangerously-bypass-approvals-and-sandbox " : "";
|
|
551
|
+
}
|
|
552
|
+
function claudeResumeOrFresh(sessionId, worktreePath, tail) {
|
|
553
|
+
if (!worktreePath) return `claude --resume ${sessionId} ${tail}\r`;
|
|
554
|
+
const munged = worktreePath.replace(/[^a-zA-Z0-9]/g, "-");
|
|
555
|
+
const sessionFile = `"$HOME/.claude/projects/${munged}/${sessionId}.jsonl"`;
|
|
556
|
+
return `if [ -f ${sessionFile} ]; then claude --resume ${sessionId} ${tail}; else claude --session-id ${sessionId} ${tail}; fi\r`;
|
|
557
|
+
}
|
|
558
|
+
function buildClaudeSettings(worktreePath) {
|
|
559
|
+
const settings = { theme: "dark" };
|
|
560
|
+
if (worktreePath) {
|
|
561
|
+
const dir = `${toShellPath(worktreePath)}/${SUBAGENTS_DIR2}`;
|
|
562
|
+
const command = `mkdir -p "${dir}" && cat > "${dir}/$(date +%s)-$$-$RANDOM.json"`;
|
|
563
|
+
const taskHook = {
|
|
564
|
+
matcher: "Task",
|
|
565
|
+
hooks: [{ type: "command", command }]
|
|
566
|
+
};
|
|
567
|
+
const eventsDir = `${toShellPath(worktreePath)}/${PROGRESS_EVENTS_DIR}`;
|
|
568
|
+
const progress = {
|
|
569
|
+
hooks: [{ type: "command", command: `mkdir -p "${eventsDir}" && cat > "${eventsDir}/$(date +%s)-$$-$RANDOM.json"; exit 0` }]
|
|
570
|
+
};
|
|
571
|
+
settings.hooks = {
|
|
572
|
+
PreToolUse: [taskHook],
|
|
573
|
+
PostToolUse: [taskHook, { matcher: TODO_TOOLS, ...progress }],
|
|
574
|
+
Stop: [progress],
|
|
575
|
+
Notification: [progress],
|
|
576
|
+
UserPromptSubmit: [progress]
|
|
577
|
+
};
|
|
578
|
+
}
|
|
579
|
+
return JSON.stringify(settings);
|
|
580
|
+
}
|
|
581
|
+
function resolveSystemPrompt(custom) {
|
|
582
|
+
return custom && custom.trim() ? custom : "";
|
|
583
|
+
}
|
|
584
|
+
function buildPrompt(task) {
|
|
585
|
+
const lines = [`\u4EFB\u52D9\u6A19\u984C\uFF1A${task.title}`];
|
|
586
|
+
const description = task.description?.trim();
|
|
587
|
+
if (description) {
|
|
588
|
+
lines.push("", "\u4EFB\u52D9\u63CF\u8FF0\uFF1A", description);
|
|
589
|
+
}
|
|
590
|
+
return lines.join("\n");
|
|
591
|
+
}
|
|
592
|
+
function executorSessionId(taskId, runId) {
|
|
593
|
+
const source = runId ? `${taskId}:${runId}` : taskId;
|
|
594
|
+
const raw = source.replace(/[^0-9a-f]/gi, "").toLowerCase();
|
|
595
|
+
const hex = runId ? `${raw.slice(0, 24).padEnd(24, "0")}${namespaceHash(`executor:${source}`)}` : raw.padEnd(32, "0").slice(0, 32);
|
|
596
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-4${hex.slice(13, 16)}-8${hex.slice(17, 20)}-${hex.slice(20, 32)}`;
|
|
597
|
+
}
|
|
598
|
+
function namespaceHash(namespace) {
|
|
599
|
+
let hash = 2166136261;
|
|
600
|
+
for (let i = 0; i < namespace.length; i += 1) {
|
|
601
|
+
hash ^= namespace.charCodeAt(i);
|
|
602
|
+
hash = Math.imul(hash, 16777619);
|
|
603
|
+
}
|
|
604
|
+
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
605
|
+
}
|
|
606
|
+
function assembleCommand(agent, systemPrompt, prompt, model, effort, opts, worktreePath, sessionId, workspacePath) {
|
|
607
|
+
let cmd;
|
|
608
|
+
if (agent === "claude") {
|
|
609
|
+
const settings = ` --settings ${shellQuote(buildClaudeSettings(worktreePath))}`;
|
|
610
|
+
const addDir = workspacePath ? ` --add-dir ${shellQuote(toShellPath(workspacePath))}` : "";
|
|
611
|
+
const modelFlag = model ? ` --model ${model}` : "";
|
|
612
|
+
const effortFlag = effort ? ` --effort ${effort}` : "";
|
|
613
|
+
const library = opts?.library;
|
|
614
|
+
const libraryFlags = library ? ` --plugin-dir ${shellQuote(toShellPath(library.pluginDir))} --add-dir ${shellQuote(toShellPath(library.libraryDir))}` : "";
|
|
615
|
+
const flags = `--chrome${claudePermissionFlag(opts?.autoMode)}${modelFlag}${effortFlag}${settings}${addDir}${libraryFlags}`;
|
|
616
|
+
const sysFlag = systemPrompt ? ` --append-system-prompt ${shellQuote(systemPrompt)}` : "";
|
|
617
|
+
const promptArg = prompt ? ` ${shellQuote(prompt)}` : "";
|
|
618
|
+
const tail = `${flags}${sysFlag}${promptArg}`;
|
|
619
|
+
cmd = sessionId && opts?.resume ? claudeResumeOrFresh(sessionId, worktreePath, tail) : `claude ${sessionId ? `--session-id ${sessionId} ` : opts?.resume ? "--continue " : ""}${tail}\r`;
|
|
620
|
+
} else {
|
|
621
|
+
const combined = [systemPrompt, prompt].filter(Boolean).join("\n\n");
|
|
622
|
+
const codexEffortFlag = effort ? `-c ${shellQuote(`model_reasoning_effort="${effort}"`)} ` : "";
|
|
623
|
+
const codexHome = opts?.library ? `CODEX_HOME=${shellQuote(toShellPath(opts.library.codexHome))} ` : "";
|
|
624
|
+
const promptArg = combined ? ` ${shellQuote(combined)}` : "";
|
|
625
|
+
cmd = `${codexHome}codex ${codexAutoFlag(opts?.autoMode)}${codexEffortFlag}--model ${model}${promptArg}\r`;
|
|
626
|
+
}
|
|
627
|
+
if (cmd.length > 2e5) console.warn(`[VibeFlow] launch command is ${cmd.length} bytes \u2014 approaching ARG_MAX`);
|
|
628
|
+
return cmd;
|
|
629
|
+
}
|
|
630
|
+
function normalizeModel(agent, model) {
|
|
631
|
+
return LEGACY_MODEL_FALLBACKS[agent]?.[model] ?? model;
|
|
632
|
+
}
|
|
633
|
+
function taskAgent(task) {
|
|
634
|
+
const agent = task.agentCli;
|
|
635
|
+
return agent && agent in AGENT_NAMES ? agent : "claude";
|
|
636
|
+
}
|
|
637
|
+
function taskModel(task) {
|
|
638
|
+
const agent = taskAgent(task);
|
|
639
|
+
return normalizeModel(agent, task.model || DEFAULT_MODELS[agent]);
|
|
640
|
+
}
|
|
641
|
+
function buildAgentCommand(task, systemPrompt, opts, workspacePath) {
|
|
642
|
+
const agent = taskAgent(task);
|
|
643
|
+
const model = taskModel(task);
|
|
644
|
+
const artifactsDir = agentArtifactsDir(task.worktreePath, workspacePath) ?? ARTIFACTS_FALLBACK_DIR;
|
|
645
|
+
const builtInPrompt = buildArtifactPrompt(artifactsDir);
|
|
646
|
+
const libraryPrompt = opts?.library?.promptText?.trim();
|
|
647
|
+
const customPrompt = resolveSystemPrompt(systemPrompt);
|
|
648
|
+
const sys = [builtInPrompt, libraryPrompt, customPrompt].filter(Boolean).join("\n\n");
|
|
649
|
+
const includeTaskPrompt = opts?.includeTaskPrompt !== false;
|
|
650
|
+
const prompt = includeTaskPrompt && !opts?.resume ? buildPrompt(task) : "";
|
|
651
|
+
const sessionId = agent === "claude" && includeTaskPrompt ? executorSessionId(task.id, task.runId) : void 0;
|
|
652
|
+
return boardEnvPrefix(task, opts) + assembleCommand(
|
|
653
|
+
agent,
|
|
654
|
+
sys,
|
|
655
|
+
prompt,
|
|
656
|
+
model,
|
|
657
|
+
task.effort,
|
|
658
|
+
opts,
|
|
659
|
+
task.worktreePath,
|
|
660
|
+
sessionId,
|
|
661
|
+
workspacePath
|
|
662
|
+
);
|
|
663
|
+
}
|
|
664
|
+
var ARTIFACTS_DIR_SUFFIX, SCRATCH_DIR_NAME, ARTIFACTS_FALLBACK_DIR, AUTO_PERMISSION_MODE, SUBAGENTS_DIR2, PROGRESS_EVENTS_DIR, TODO_TOOLS, AGENT_NAMES, DEFAULT_MODELS, LEGACY_MODEL_FALLBACKS;
|
|
665
|
+
var init_launch = __esm({
|
|
666
|
+
"packages/core/src/launch.ts"() {
|
|
667
|
+
"use strict";
|
|
668
|
+
ARTIFACTS_DIR_SUFFIX = ".artifacts";
|
|
669
|
+
SCRATCH_DIR_NAME = "scratch";
|
|
670
|
+
ARTIFACTS_FALLBACK_DIR = ".vibeflow-artifacts";
|
|
671
|
+
AUTO_PERMISSION_MODE = "auto";
|
|
672
|
+
SUBAGENTS_DIR2 = ".vibeflow-subagents";
|
|
673
|
+
PROGRESS_EVENTS_DIR = ".vibeflow-events";
|
|
674
|
+
TODO_TOOLS = "TodoWrite|TaskCreate|TaskUpdate";
|
|
675
|
+
AGENT_NAMES = {
|
|
676
|
+
claude: "Claude Code",
|
|
677
|
+
codex: "Codex CLI"
|
|
678
|
+
};
|
|
679
|
+
DEFAULT_MODELS = {
|
|
680
|
+
claude: "sonnet",
|
|
681
|
+
codex: "gpt-5.5"
|
|
682
|
+
};
|
|
683
|
+
LEGACY_MODEL_FALLBACKS = {
|
|
684
|
+
codex: {
|
|
685
|
+
"gpt-5-codex": "gpt-5.5",
|
|
686
|
+
"gpt-5": "gpt-5.5"
|
|
687
|
+
}
|
|
688
|
+
};
|
|
689
|
+
}
|
|
690
|
+
});
|
|
691
|
+
|
|
502
692
|
// packages/core/src/attachments.ts
|
|
503
693
|
import { randomUUID } from "crypto";
|
|
504
694
|
import { execFileSync as execFileSync3 } from "child_process";
|
|
@@ -599,6 +789,33 @@ var init_attachments = __esm({
|
|
|
599
789
|
}
|
|
600
790
|
});
|
|
601
791
|
|
|
792
|
+
// packages/core/src/effort.ts
|
|
793
|
+
function isAgentEffort(value) {
|
|
794
|
+
return typeof value === "string" && AGENT_EFFORTS.includes(value);
|
|
795
|
+
}
|
|
796
|
+
function allowedEfforts(model) {
|
|
797
|
+
return model?.efforts ?? null;
|
|
798
|
+
}
|
|
799
|
+
function selectableEfforts(model) {
|
|
800
|
+
return allowedEfforts(model) ?? UNKNOWN_MODEL_EFFORTS;
|
|
801
|
+
}
|
|
802
|
+
function clampEffort(effort, allowed) {
|
|
803
|
+
if (allowed.length === 0) return void 0;
|
|
804
|
+
if (effort === void 0 || allowed.includes(effort)) return effort;
|
|
805
|
+
const rank = AGENT_EFFORTS.indexOf(effort);
|
|
806
|
+
const ordered = AGENT_EFFORTS.filter((level) => allowed.includes(level));
|
|
807
|
+
const notDeeper = ordered.filter((level) => AGENT_EFFORTS.indexOf(level) <= rank);
|
|
808
|
+
return notDeeper.length > 0 ? notDeeper[notDeeper.length - 1] : ordered[0];
|
|
809
|
+
}
|
|
810
|
+
var AGENT_EFFORTS, UNKNOWN_MODEL_EFFORTS;
|
|
811
|
+
var init_effort = __esm({
|
|
812
|
+
"packages/core/src/effort.ts"() {
|
|
813
|
+
"use strict";
|
|
814
|
+
AGENT_EFFORTS = ["low", "medium", "high", "xhigh", "max", "ultra"];
|
|
815
|
+
UNKNOWN_MODEL_EFFORTS = ["low", "medium", "high", "xhigh"];
|
|
816
|
+
}
|
|
817
|
+
});
|
|
818
|
+
|
|
602
819
|
// packages/core/src/progress.ts
|
|
603
820
|
function emptyUsage() {
|
|
604
821
|
return { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, reasoning: 0 };
|
|
@@ -883,9 +1100,9 @@ var init_progress = __esm({
|
|
|
883
1100
|
|
|
884
1101
|
// packages/tui/src/task-form.ts
|
|
885
1102
|
import { spawnSync } from "child_process";
|
|
886
|
-
import
|
|
887
|
-
import
|
|
888
|
-
import
|
|
1103
|
+
import fs22 from "fs";
|
|
1104
|
+
import os9 from "os";
|
|
1105
|
+
import path26 from "path";
|
|
889
1106
|
import React, { useEffect, useRef, useState } from "react";
|
|
890
1107
|
import { Box, Text, render, useApp, useInput } from "ink";
|
|
891
1108
|
function newFormState(defaultAutoMode, agents) {
|
|
@@ -951,11 +1168,19 @@ function visibleFields(s, ctx) {
|
|
|
951
1168
|
return fields;
|
|
952
1169
|
}
|
|
953
1170
|
function modelOptions(ctx, agentCli, current) {
|
|
954
|
-
const listed = ctx.models[agentCli] ?? [];
|
|
1171
|
+
const listed = (ctx.models[agentCli] ?? []).map((m) => m.id);
|
|
955
1172
|
const options = ["", ...listed];
|
|
956
1173
|
if (current && !listed.includes(current)) options.push(current);
|
|
957
1174
|
return options;
|
|
958
1175
|
}
|
|
1176
|
+
function effortOptions(ctx, agentCli, model) {
|
|
1177
|
+
const id = taskModel({ agentCli, model });
|
|
1178
|
+
return selectableEfforts(ctx.models[agentCli]?.find((m) => m.id === id));
|
|
1179
|
+
}
|
|
1180
|
+
function fitEffort(ctx, s) {
|
|
1181
|
+
const effort = clampEffort(s.effort, effortOptions(ctx, s.agentCli, s.model)) ?? s.effort;
|
|
1182
|
+
return effort === s.effort ? s : { ...s, effort };
|
|
1183
|
+
}
|
|
959
1184
|
function blockReason(s, ctx, loadingGit) {
|
|
960
1185
|
if (!s.title.trim()) return "\u8ACB\u8F38\u5165\u4EFB\u52D9\u6A19\u984C";
|
|
961
1186
|
if (loadingGit) return "\u5075\u6E2C Git \u72C0\u614B\u4E2D\u2026";
|
|
@@ -1004,17 +1229,17 @@ function editorCommand(env, platform) {
|
|
|
1004
1229
|
return env.VISUAL?.trim() || env.EDITOR?.trim() || (platform === "win32" ? "notepad" : "vi");
|
|
1005
1230
|
}
|
|
1006
1231
|
function editInEditor(text) {
|
|
1007
|
-
const dir =
|
|
1008
|
-
const file =
|
|
1232
|
+
const dir = fs22.mkdtempSync(path26.join(os9.tmpdir(), "vibeflow-"));
|
|
1233
|
+
const file = path26.join(dir, "description.md");
|
|
1009
1234
|
try {
|
|
1010
|
-
|
|
1235
|
+
fs22.writeFileSync(file, text);
|
|
1011
1236
|
const cmd = editorCommand(process.env, process.platform);
|
|
1012
1237
|
const r = spawnSync(`${cmd} "${file}"`, { stdio: "inherit", shell: true });
|
|
1013
1238
|
if (r.error) return { error: `\u7121\u6CD5\u555F\u52D5\u7DE8\u8F2F\u5668\uFF08${cmd}\uFF09\uFF1A${r.error.message}` };
|
|
1014
1239
|
if (r.status !== 0) return { error: `\u7DE8\u8F2F\u5668\uFF08${cmd}\uFF09\u7D50\u675F\u78BC ${r.status}\uFF0C\u63CF\u8FF0\u672A\u8B8A\u66F4` };
|
|
1015
|
-
return { text:
|
|
1240
|
+
return { text: fs22.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
|
|
1016
1241
|
} finally {
|
|
1017
|
-
|
|
1242
|
+
fs22.rmSync(dir, { recursive: true, force: true });
|
|
1018
1243
|
}
|
|
1019
1244
|
}
|
|
1020
1245
|
function cycle(list, current, step) {
|
|
@@ -1085,10 +1310,10 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
1085
1310
|
if (editing === "title") setS((prev) => ({ ...prev, title: draft }));
|
|
1086
1311
|
else if (editing === "branch") setS((prev) => ({ ...prev, branch: value2 }));
|
|
1087
1312
|
else if (editing === "project") {
|
|
1088
|
-
if (value2) setProject(
|
|
1313
|
+
if (value2) setProject(path26.resolve(value2.replace(/^~(?=$|\/)/, os9.homedir())));
|
|
1089
1314
|
} else if (editing === "attachments" && value2) {
|
|
1090
|
-
const file =
|
|
1091
|
-
if (!
|
|
1315
|
+
const file = path26.resolve(value2.replace(/^~(?=$|\/)/, os9.homedir()));
|
|
1316
|
+
if (!fs22.existsSync(file) || !fs22.statSync(file).isFile()) {
|
|
1092
1317
|
setMessage(`\u627E\u4E0D\u5230\u6A94\u6848\uFF1A${file}`);
|
|
1093
1318
|
return;
|
|
1094
1319
|
}
|
|
@@ -1127,13 +1352,16 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
1127
1352
|
} else if (field === "baseBranch") {
|
|
1128
1353
|
setS((prev) => ({ ...prev, baseBranch: cycle(prev.gitInfo?.branches ?? [], prev.baseBranch, step) }));
|
|
1129
1354
|
} else if (field === "effort") {
|
|
1130
|
-
setS((prev) =>
|
|
1355
|
+
setS((prev) => {
|
|
1356
|
+
const levels = effortOptions(ctx, prev.agentCli, prev.model);
|
|
1357
|
+
return levels.length ? { ...prev, effort: cycle([...levels], prev.effort, step) } : prev;
|
|
1358
|
+
});
|
|
1131
1359
|
} else if (field === "autoMode") {
|
|
1132
1360
|
setS((prev) => ({ ...prev, autoMode: !prev.autoMode }));
|
|
1133
1361
|
} else if (field === "agent" && ctx.agents?.length) {
|
|
1134
|
-
setS((prev) => ({ ...prev, agentCli: cycle(ctx.agents.map((a) => a.id), prev.agentCli, step), model: "" }));
|
|
1362
|
+
setS((prev) => fitEffort(ctx, { ...prev, agentCli: cycle(ctx.agents.map((a) => a.id), prev.agentCli, step), model: "" }));
|
|
1135
1363
|
} else if (field === "model") {
|
|
1136
|
-
setS((prev) => ({ ...prev, model: cycle(modelOptions(ctx, prev.agentCli, prev.model), prev.model, step) }));
|
|
1364
|
+
setS((prev) => fitEffort(ctx, { ...prev, model: cycle(modelOptions(ctx, prev.agentCli, prev.model), prev.model, step) }));
|
|
1137
1365
|
}
|
|
1138
1366
|
return;
|
|
1139
1367
|
}
|
|
@@ -1168,14 +1396,15 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
1168
1396
|
case "branch":
|
|
1169
1397
|
return s.branch || "\uFF08\u7559\u7A7A\u5247\u81EA\u52D5\u547D\u540D\uFF0C\u4F8B\u5982 feature/login-fix\uFF09";
|
|
1170
1398
|
case "attachments":
|
|
1171
|
-
return s.attachments.length ? s.attachments.map((a) =>
|
|
1399
|
+
return s.attachments.length ? s.attachments.map((a) => path26.basename(a)).join("\u3001") : "\uFF08Enter \u8F38\u5165\u6A94\u6848\u8DEF\u5F91\u52A0\u5165\uFF09";
|
|
1172
1400
|
case "description": {
|
|
1173
1401
|
if (!s.description) return "\uFF08Enter \u7528\u7DE8\u8F2F\u5668\u64B0\u5BEB\uFF09";
|
|
1174
1402
|
const lines = s.description.split("\n");
|
|
1175
1403
|
return lines.length > 1 ? `${lines[0]} \u2026\uFF08\u5171 ${lines.length} \u884C\uFF09` : lines[0];
|
|
1176
1404
|
}
|
|
1177
1405
|
case "effort": {
|
|
1178
|
-
|
|
1406
|
+
if (effortOptions(ctx, s.agentCli, s.model).length === 0) return "\u4E0D\u9069\u7528";
|
|
1407
|
+
const e = EFFORTS.find((x) => x.value === s.effort) ?? EFFORTS[0];
|
|
1179
1408
|
return `\u2039 ${e.label} \xB7 ${e.value} \u203A`;
|
|
1180
1409
|
}
|
|
1181
1410
|
case "autoMode":
|
|
@@ -1185,8 +1414,11 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
1185
1414
|
if (ctx.agents.length === 0) return "\u672A\u5075\u6E2C\u5230 Agent CLI\uFF08claude / codex\uFF09";
|
|
1186
1415
|
return `\u2039 ${ctx.agents.find((a) => a.id === s.agentCli)?.name ?? s.agentCli} \u203A`;
|
|
1187
1416
|
}
|
|
1188
|
-
case "model":
|
|
1189
|
-
|
|
1417
|
+
case "model": {
|
|
1418
|
+
if (modelOptions(ctx, s.agentCli, s.model).length <= 1) return "\u7121\u6CD5\u53D6\u5F97 model list\uFF0C\u5C07\u4F7F\u7528\u9810\u8A2D model";
|
|
1419
|
+
const listed = ctx.models[s.agentCli]?.find((m) => m.id === s.model);
|
|
1420
|
+
return `\u2039 ${s.model ? listed?.label ?? s.model : "\u4F7F\u7528\u9810\u8A2D model"} \u203A`;
|
|
1421
|
+
}
|
|
1190
1422
|
case "submit":
|
|
1191
1423
|
return busy ?? (s.mode === "new" ? "\uFF3B \u5EFA\u7ACB\u4EFB\u52D9 \uFF3D" : "\uFF3B \u5132\u5B58\u8B8A\u66F4 \uFF3D");
|
|
1192
1424
|
}
|
|
@@ -1199,13 +1431,17 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
1199
1431
|
if (info && !info.isRepo) return "\u9019\u500B\u8CC7\u6599\u593E\u4E0D\u662F Git repository\u3002\u8ACB\u6539\u9078\u4E00\u500B Git \u5C08\u6848\uFF0C\u6216\u5728\u4E0B\u65B9\u521D\u59CB\u5316\u3002";
|
|
1200
1432
|
if (s.mode === "edit" && projectChanged(s)) return "\u5206\u652F\u6703\u5728\u65B0\u5C08\u6848\u3001\u5361\u7247\u958B\u59CB\u57F7\u884C\u6642\u624D\u5EFA\u7ACB\u3002";
|
|
1201
1433
|
if (info?.isRepo && !info.hasRemote) return `\u6B64 repository \u6C92\u6709 remote\uFF0C\u5C07\u4EE5\u76EE\u524D\u5206\u652F (${info.currentBranch ?? "HEAD"}) \u70BA\u57FA\u6E96\u5EFA\u7ACB\u672C\u5730 worktree\u3002`;
|
|
1202
|
-
if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${
|
|
1434
|
+
if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${path26.basename(s.projectPath)}`;
|
|
1203
1435
|
}
|
|
1204
1436
|
if (field === "branch") {
|
|
1205
1437
|
const when = info?.hasRemote ? "\u958B\u59CB\u57F7\u884C\u6642\u6703\u5EFA\u7ACB\u540C\u540D\u5206\u652F\u4E26\u63A8\u5230 origin\uFF1B\u82E5 remote \u5DF2\u6709\u9019\u500B\u5206\u652F\uFF0C\u5247\u76F4\u63A5\u53D6\u56DE\u4E26\u63A5\u7E8C\u4E0A\u9762\u7684\u5DE5\u4F5C\u3002" : "\u958B\u59CB\u57F7\u884C\u6642\u6703\u5EFA\u7ACB\u540C\u540D\u5206\u652F\u3002";
|
|
1206
1438
|
return s.mode === "edit" && s.worktreePath ? `${when} \u9019\u5F35\u5361\u5DF2\u6709 worktree\uFF1A\u66F4\u6539\u5206\u652F\u3001\u5C08\u6848\u6216\u57FA\u6E96\u5206\u652F\u6703\u79FB\u9664\u5B83\uFF08\u6709\u672A\u5B8C\u6210\u8B8A\u66F4\u6642\u7121\u6CD5\u5132\u5B58\uFF09\u3002` : when;
|
|
1207
1439
|
}
|
|
1208
|
-
if (field === "effort")
|
|
1440
|
+
if (field === "effort") {
|
|
1441
|
+
if (effortOptions(ctx, s.agentCli, s.model).length === 0) return "\u9019\u500B model \u4E0D\u652F\u63F4\u8ABF\u6574\u63A8\u7406\u5F37\u5EA6\uFF0C\u555F\u52D5\u6642\u4E0D\u6703\u6307\u5B9A\u4EFB\u52D9\u8907\u96DC\u5EA6\u3002";
|
|
1442
|
+
const e = EFFORTS.find((x) => x.value === s.effort) ?? EFFORTS[0];
|
|
1443
|
+
return `${e.description} Claude Code \u8207 Codex \u6703\u5728\u555F\u52D5\u6B64\u4EFB\u52D9\u6642\u5957\u7528\uFF1B\u53EF\u9078\u7684\u7D1A\u8DDD\u4F9D model \u800C\u5B9A\u3002`;
|
|
1444
|
+
}
|
|
1209
1445
|
if (field === "autoMode") return s.autoMode ? "\u958B\u555F\uFF1AAgent \u6703\u76F4\u63A5\u4FEE\u6539\u6A94\u6848\u3001\u57F7\u884C\u6307\u4EE4\uFF0C\u4E0D\u6703\u9010\u6B21\u5411\u4F60\u78BA\u8A8D\u3002" : "\u95DC\u9589\uFF1AAgent \u6BCF\u6B21\u8981\u52D5\u4F5C\u524D\uFF0C\u90FD\u6703\u5728\u7D42\u7AEF\u6A5F\u88E1\u7B49\u4F60\u5141\u8A31\u3002";
|
|
1210
1446
|
if (field === "submit") return blockReason(s, ctx, loadingGit);
|
|
1211
1447
|
return null;
|
|
@@ -1260,7 +1496,7 @@ async function loadFormContext(api) {
|
|
|
1260
1496
|
const lists = await Promise.all(
|
|
1261
1497
|
(agents ?? []).map(async (a) => {
|
|
1262
1498
|
const list = await api.listAgentModels(a.id).catch(() => null);
|
|
1263
|
-
return [a.id, list ? list.models
|
|
1499
|
+
return [a.id, list ? list.models : []];
|
|
1264
1500
|
})
|
|
1265
1501
|
);
|
|
1266
1502
|
return {
|
|
@@ -1277,13 +1513,17 @@ var h, EFFORTS, DEFAULT_EFFORT, LABELS, TEXT_FIELDS;
|
|
|
1277
1513
|
var init_task_form = __esm({
|
|
1278
1514
|
"packages/tui/src/task-form.ts"() {
|
|
1279
1515
|
"use strict";
|
|
1516
|
+
init_effort();
|
|
1517
|
+
init_launch();
|
|
1280
1518
|
init_attachments();
|
|
1281
1519
|
h = React.createElement;
|
|
1282
1520
|
EFFORTS = [
|
|
1283
1521
|
{ value: "low", label: "\u5FEB\u901F", description: "\u9069\u5408\u7BC4\u570D\u660E\u78BA\u3001\u4F4E\u98A8\u96AA\u7684\u5C0F\u4EFB\u52D9\u3002" },
|
|
1284
1522
|
{ value: "medium", label: "\u6A19\u6E96", description: "\u517C\u9867\u901F\u5EA6\u8207\u63A8\u7406\u6DF1\u5EA6\uFF0C\u9069\u5408\u4E00\u822C\u958B\u767C\u4EFB\u52D9\u3002" },
|
|
1285
1523
|
{ value: "high", label: "\u6DF1\u5165", description: "\u9069\u5408\u8DE8\u6A94\u6848\u3001\u9664\u932F\u6216\u9700\u8981\u8F03\u591A\u5224\u65B7\u7684\u4EFB\u52D9\u3002" },
|
|
1286
|
-
{ value: "xhigh", label: "\u6975\u81F4", description: "\u9069\u5408\u9AD8\u8907\u96DC\u5EA6\u3001\u9577\u93C8\u63A8\u7406\u6216\u9AD8\u98A8\u96AA\u8B8A\u66F4\u3002" }
|
|
1524
|
+
{ value: "xhigh", label: "\u6975\u81F4", description: "\u9069\u5408\u9AD8\u8907\u96DC\u5EA6\u3001\u9577\u93C8\u63A8\u7406\u6216\u9AD8\u98A8\u96AA\u8B8A\u66F4\u3002" },
|
|
1525
|
+
{ value: "max", label: "\u6700\u5927", description: "\u6700\u6DF1\u7684\u63A8\u7406\uFF0C\u9069\u5408\u6700\u56F0\u96E3\u7684\u554F\u984C\u3002" },
|
|
1526
|
+
{ value: "ultra", label: "\u59D4\u6D3E", description: "\u6700\u6DF1\u63A8\u7406\u4E26\u81EA\u52D5\u59D4\u6D3E\u5B50\u4EFB\u52D9\uFF0C\u8017\u6642\u8207\u7528\u91CF\u53EF\u80FD\u660E\u986F\u589E\u52A0\u3002" }
|
|
1287
1527
|
];
|
|
1288
1528
|
DEFAULT_EFFORT = "medium";
|
|
1289
1529
|
LABELS = {
|
|
@@ -1319,7 +1559,7 @@ __export(src_exports, {
|
|
|
1319
1559
|
showDiff: () => showDiff
|
|
1320
1560
|
});
|
|
1321
1561
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
1322
|
-
import
|
|
1562
|
+
import path27 from "path";
|
|
1323
1563
|
import React2, { useEffect as useEffect2, useState as useState2 } from "react";
|
|
1324
1564
|
import { Box as Box2, Text as Text2, render as render2, useApp as useApp2, useInput as useInput2, useStdout } from "ink";
|
|
1325
1565
|
function findDetachKey(text) {
|
|
@@ -1615,7 +1855,7 @@ function showDiff(task) {
|
|
|
1615
1855
|
});
|
|
1616
1856
|
}
|
|
1617
1857
|
function tmuxAttach(opts, sessionName) {
|
|
1618
|
-
const conf =
|
|
1858
|
+
const conf = path27.join(opts.userDataDir, "tmux.conf");
|
|
1619
1859
|
const inOurServer = (process.env.TMUX ?? "").split(",")[0].endsWith(`/${TMUX_SOCKET}`);
|
|
1620
1860
|
const args = inOurServer ? ["-L", TMUX_SOCKET, "-f", conf, "switch-client", "-t", `=${sessionName}`] : ["-L", TMUX_SOCKET, "-f", conf, "attach-session", "-t", `=${sessionName}`];
|
|
1621
1861
|
spawnSync2("tmux", args, { stdio: "inherit", env: { ...process.env, TMUX: inOurServer ? process.env.TMUX : "" } });
|
|
@@ -1746,8 +1986,8 @@ var init_src = __esm({
|
|
|
1746
1986
|
});
|
|
1747
1987
|
|
|
1748
1988
|
// packages/cli/src/main.ts
|
|
1749
|
-
import
|
|
1750
|
-
import
|
|
1989
|
+
import fs23 from "fs";
|
|
1990
|
+
import path28 from "path";
|
|
1751
1991
|
import { fileURLToPath } from "url";
|
|
1752
1992
|
import { parseArgs as parseArgs2 } from "node:util";
|
|
1753
1993
|
|
|
@@ -2699,191 +2939,8 @@ function unwatchAllSubAgents() {
|
|
|
2699
2939
|
}
|
|
2700
2940
|
}
|
|
2701
2941
|
|
|
2702
|
-
// packages/core/src/launch.ts
|
|
2703
|
-
var ARTIFACTS_DIR_SUFFIX = ".artifacts";
|
|
2704
|
-
var SCRATCH_DIR_NAME = "scratch";
|
|
2705
|
-
var ARTIFACTS_FALLBACK_DIR = ".vibeflow-artifacts";
|
|
2706
|
-
function pathBasename(p) {
|
|
2707
|
-
const norm = p.replace(/\\/g, "/").replace(/\/+$/, "");
|
|
2708
|
-
const i = norm.lastIndexOf("/");
|
|
2709
|
-
return i >= 0 ? norm.slice(i + 1) : norm;
|
|
2710
|
-
}
|
|
2711
|
-
function agentArtifactsDir(worktreePath, workspacePath) {
|
|
2712
|
-
if (!worktreePath || !workspacePath) return null;
|
|
2713
|
-
const dir = toShellPath(workspacePath);
|
|
2714
|
-
const ws = pathBasename(worktreePath);
|
|
2715
|
-
return `${dir}/${ws}${ARTIFACTS_DIR_SUFFIX}`;
|
|
2716
|
-
}
|
|
2717
|
-
function buildArtifactPrompt(artifactsDir = ARTIFACTS_FALLBACK_DIR) {
|
|
2718
|
-
return [
|
|
2719
|
-
"Artifact \u8A2D\u5B9A\uFF08\u672C\u6B21 session \u5168\u7A0B\u9069\u7528\uFF09\uFF1A",
|
|
2720
|
-
`1. \u672C\u4EFB\u52D9\u7684 Artifact \u8CC7\u6599\u593E\u662F ${artifactsDir}/\uFF1B\u9700\u8981\u4FDD\u5B58\u7D66\u4F7F\u7528\u8005\u7684\u9A57\u8B49\u8B49\u64DA\u6216\u5831\u544A\u6642\uFF0C\u8ACB\u5BEB\u5165\u6B64\u8CC7\u6599\u593E\uFF08\u4E0D\u5B58\u5728\u8ACB\u5EFA\u7ACB\uFF09\u3002`,
|
|
2721
|
-
`2. \u4F7F\u7528\u8005\u6703\u5728 VibeFlow \u7684 Artifacts \u5206\u9801\u6AA2\u8996\u6839\u76EE\u9304\u5167\u5BB9\uFF1B\u53EA\u653E\u4F60\u6703\u4E3B\u52D5\u8ACB\u4F7F\u7528\u8005\u904E\u76EE\u7684\u6A94\u6848\u3002`,
|
|
2722
|
-
`3. \u4F60\u81EA\u5DF1\u7684\u5DE5\u4F5C\u66AB\u5B58\uFF08\u4E00\u6B21\u6027 script\u3001log\u3001\u4E2D\u9593\u8F38\u51FA\u3001debug \u6A94\uFF09\u653E\u5728 ${artifactsDir}/${SCRATCH_DIR_NAME}/\u3002`,
|
|
2723
|
-
`4. \u82E5\u5DE5\u5177\u5148\u8F38\u51FA\u5230 /tmp\u3001/private/tmp\u3001$TMPDIR \u6216 ~/Downloads\uFF0C\u8ACB\u628A\u6700\u7D42\u8981\u5448\u73FE\u7684\u6A94\u6848\u8907\u88FD\u5230 ${artifactsDir}/ \u4E26\u78BA\u8A8D\u5B58\u5728\u3002`,
|
|
2724
|
-
"5. Artifact \u8CC7\u6599\u593E\u4F4D\u65BC worktree \u4E4B\u5916\uFF1B\u4E0D\u8981\u628A\u5176\u4E2D\u5167\u5BB9\u52A0\u5165 git commit\uFF0C\u4E5F\u4E0D\u8981\u628A\u6700\u7D42\u4EA4\u4ED8\u7269\u53EA\u7559\u5728\u9019\u88E1\u3002"
|
|
2725
|
-
].join("\n");
|
|
2726
|
-
}
|
|
2727
|
-
var AUTO_PERMISSION_MODE = "auto";
|
|
2728
|
-
function claudePermissionFlag(autoMode) {
|
|
2729
|
-
return autoMode ? ` --permission-mode ${AUTO_PERMISSION_MODE}` : "";
|
|
2730
|
-
}
|
|
2731
|
-
function shellQuote(s) {
|
|
2732
|
-
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
2733
|
-
}
|
|
2734
|
-
function toShellPath(p) {
|
|
2735
|
-
return p.replace(/\\/g, "/");
|
|
2736
|
-
}
|
|
2737
|
-
function boardEnvPrefix(task, opts) {
|
|
2738
|
-
const board = opts?.boardCli;
|
|
2739
|
-
if (!board) return "";
|
|
2740
|
-
const entries = [
|
|
2741
|
-
["VIBEFLOW_TASK_ID", task.id],
|
|
2742
|
-
["VIBEFLOW_PROJECT_PATH", task.projectPath],
|
|
2743
|
-
["VIBEFLOW_BRANCH", task.branch],
|
|
2744
|
-
["VIBEFLOW_BASE_BRANCH", task.baseBranch],
|
|
2745
|
-
["VIBEFLOW_STORE_DIR", board?.storeDir],
|
|
2746
|
-
["VIBEFLOW_CLI", board?.cliPath],
|
|
2747
|
-
["VIBEFLOW_CLI_LOADER", board?.loaderPath],
|
|
2748
|
-
["VIBEFLOW_AUTO_MODE", opts?.autoMode ? "1" : "0"]
|
|
2749
|
-
];
|
|
2750
|
-
const exports = entries.filter((entry) => Boolean(entry[1])).map(([key, value]) => `${key}=${shellQuote(toShellPath(value))}`);
|
|
2751
|
-
return exports.length ? `export ${exports.join(" ")}; ` : "";
|
|
2752
|
-
}
|
|
2753
|
-
function codexAutoFlag(autoMode) {
|
|
2754
|
-
return autoMode ? "--dangerously-bypass-approvals-and-sandbox " : "";
|
|
2755
|
-
}
|
|
2756
|
-
function claudeResumeOrFresh(sessionId, worktreePath, tail) {
|
|
2757
|
-
if (!worktreePath) return `claude --resume ${sessionId} ${tail}\r`;
|
|
2758
|
-
const munged = worktreePath.replace(/[^a-zA-Z0-9]/g, "-");
|
|
2759
|
-
const sessionFile = `"$HOME/.claude/projects/${munged}/${sessionId}.jsonl"`;
|
|
2760
|
-
return `if [ -f ${sessionFile} ]; then claude --resume ${sessionId} ${tail}; else claude --session-id ${sessionId} ${tail}; fi\r`;
|
|
2761
|
-
}
|
|
2762
|
-
var SUBAGENTS_DIR2 = ".vibeflow-subagents";
|
|
2763
|
-
var PROGRESS_EVENTS_DIR = ".vibeflow-events";
|
|
2764
|
-
var TODO_TOOLS = "TodoWrite|TaskCreate|TaskUpdate";
|
|
2765
|
-
function buildClaudeSettings(worktreePath) {
|
|
2766
|
-
const settings = { theme: "dark" };
|
|
2767
|
-
if (worktreePath) {
|
|
2768
|
-
const dir = `${toShellPath(worktreePath)}/${SUBAGENTS_DIR2}`;
|
|
2769
|
-
const command = `mkdir -p "${dir}" && cat > "${dir}/$(date +%s)-$$-$RANDOM.json"`;
|
|
2770
|
-
const taskHook = {
|
|
2771
|
-
matcher: "Task",
|
|
2772
|
-
hooks: [{ type: "command", command }]
|
|
2773
|
-
};
|
|
2774
|
-
const eventsDir = `${toShellPath(worktreePath)}/${PROGRESS_EVENTS_DIR}`;
|
|
2775
|
-
const progress = {
|
|
2776
|
-
hooks: [{ type: "command", command: `mkdir -p "${eventsDir}" && cat > "${eventsDir}/$(date +%s)-$$-$RANDOM.json"; exit 0` }]
|
|
2777
|
-
};
|
|
2778
|
-
settings.hooks = {
|
|
2779
|
-
PreToolUse: [taskHook],
|
|
2780
|
-
PostToolUse: [taskHook, { matcher: TODO_TOOLS, ...progress }],
|
|
2781
|
-
Stop: [progress],
|
|
2782
|
-
Notification: [progress],
|
|
2783
|
-
UserPromptSubmit: [progress]
|
|
2784
|
-
};
|
|
2785
|
-
}
|
|
2786
|
-
return JSON.stringify(settings);
|
|
2787
|
-
}
|
|
2788
|
-
function resolveSystemPrompt(custom) {
|
|
2789
|
-
return custom && custom.trim() ? custom : "";
|
|
2790
|
-
}
|
|
2791
|
-
function buildPrompt(task) {
|
|
2792
|
-
const lines = [`\u4EFB\u52D9\u6A19\u984C\uFF1A${task.title}`];
|
|
2793
|
-
const description = task.description?.trim();
|
|
2794
|
-
if (description) {
|
|
2795
|
-
lines.push("", "\u4EFB\u52D9\u63CF\u8FF0\uFF1A", description);
|
|
2796
|
-
}
|
|
2797
|
-
return lines.join("\n");
|
|
2798
|
-
}
|
|
2799
|
-
function executorSessionId(taskId, runId) {
|
|
2800
|
-
const source = runId ? `${taskId}:${runId}` : taskId;
|
|
2801
|
-
const raw = source.replace(/[^0-9a-f]/gi, "").toLowerCase();
|
|
2802
|
-
const hex = runId ? `${raw.slice(0, 24).padEnd(24, "0")}${namespaceHash(`executor:${source}`)}` : raw.padEnd(32, "0").slice(0, 32);
|
|
2803
|
-
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-4${hex.slice(13, 16)}-8${hex.slice(17, 20)}-${hex.slice(20, 32)}`;
|
|
2804
|
-
}
|
|
2805
|
-
function namespaceHash(namespace) {
|
|
2806
|
-
let hash = 2166136261;
|
|
2807
|
-
for (let i = 0; i < namespace.length; i += 1) {
|
|
2808
|
-
hash ^= namespace.charCodeAt(i);
|
|
2809
|
-
hash = Math.imul(hash, 16777619);
|
|
2810
|
-
}
|
|
2811
|
-
return (hash >>> 0).toString(16).padStart(8, "0");
|
|
2812
|
-
}
|
|
2813
|
-
function assembleCommand(agent, systemPrompt, prompt, model, effort, opts, worktreePath, sessionId, workspacePath) {
|
|
2814
|
-
let cmd;
|
|
2815
|
-
if (agent === "claude") {
|
|
2816
|
-
const settings = ` --settings ${shellQuote(buildClaudeSettings(worktreePath))}`;
|
|
2817
|
-
const addDir = workspacePath ? ` --add-dir ${shellQuote(toShellPath(workspacePath))}` : "";
|
|
2818
|
-
const modelFlag = model ? ` --model ${model}` : "";
|
|
2819
|
-
const effortFlag = effort ? ` --effort ${effort}` : "";
|
|
2820
|
-
const library = opts?.library;
|
|
2821
|
-
const libraryFlags = library ? ` --plugin-dir ${shellQuote(toShellPath(library.pluginDir))} --add-dir ${shellQuote(toShellPath(library.libraryDir))}` : "";
|
|
2822
|
-
const flags = `--chrome${claudePermissionFlag(opts?.autoMode)}${modelFlag}${effortFlag}${settings}${addDir}${libraryFlags}`;
|
|
2823
|
-
const sysFlag = systemPrompt ? ` --append-system-prompt ${shellQuote(systemPrompt)}` : "";
|
|
2824
|
-
const promptArg = prompt ? ` ${shellQuote(prompt)}` : "";
|
|
2825
|
-
const tail = `${flags}${sysFlag}${promptArg}`;
|
|
2826
|
-
cmd = sessionId && opts?.resume ? claudeResumeOrFresh(sessionId, worktreePath, tail) : `claude ${sessionId ? `--session-id ${sessionId} ` : opts?.resume ? "--continue " : ""}${tail}\r`;
|
|
2827
|
-
} else {
|
|
2828
|
-
const combined = [systemPrompt, prompt].filter(Boolean).join("\n\n");
|
|
2829
|
-
const codexEffortFlag = effort ? `-c ${shellQuote(`model_reasoning_effort="${effort}"`)} ` : "";
|
|
2830
|
-
const codexHome = opts?.library ? `CODEX_HOME=${shellQuote(toShellPath(opts.library.codexHome))} ` : "";
|
|
2831
|
-
const promptArg = combined ? ` ${shellQuote(combined)}` : "";
|
|
2832
|
-
cmd = `${codexHome}codex ${codexAutoFlag(opts?.autoMode)}${codexEffortFlag}--model ${model}${promptArg}\r`;
|
|
2833
|
-
}
|
|
2834
|
-
if (cmd.length > 2e5) console.warn(`[VibeFlow] launch command is ${cmd.length} bytes \u2014 approaching ARG_MAX`);
|
|
2835
|
-
return cmd;
|
|
2836
|
-
}
|
|
2837
|
-
var AGENT_NAMES = {
|
|
2838
|
-
claude: "Claude Code",
|
|
2839
|
-
codex: "Codex CLI"
|
|
2840
|
-
};
|
|
2841
|
-
var DEFAULT_MODELS = {
|
|
2842
|
-
claude: "sonnet",
|
|
2843
|
-
codex: "gpt-5.5"
|
|
2844
|
-
};
|
|
2845
|
-
var LEGACY_MODEL_FALLBACKS = {
|
|
2846
|
-
codex: {
|
|
2847
|
-
"gpt-5-codex": "gpt-5.5",
|
|
2848
|
-
"gpt-5": "gpt-5.5"
|
|
2849
|
-
}
|
|
2850
|
-
};
|
|
2851
|
-
function normalizeModel(agent, model) {
|
|
2852
|
-
return LEGACY_MODEL_FALLBACKS[agent]?.[model] ?? model;
|
|
2853
|
-
}
|
|
2854
|
-
function taskAgent(task) {
|
|
2855
|
-
const agent = task.agentCli;
|
|
2856
|
-
return agent && agent in AGENT_NAMES ? agent : "claude";
|
|
2857
|
-
}
|
|
2858
|
-
function taskModel(task) {
|
|
2859
|
-
const agent = taskAgent(task);
|
|
2860
|
-
return normalizeModel(agent, task.model || DEFAULT_MODELS[agent]);
|
|
2861
|
-
}
|
|
2862
|
-
function buildAgentCommand(task, systemPrompt, opts, workspacePath) {
|
|
2863
|
-
const agent = taskAgent(task);
|
|
2864
|
-
const model = taskModel(task);
|
|
2865
|
-
const artifactsDir = agentArtifactsDir(task.worktreePath, workspacePath) ?? ARTIFACTS_FALLBACK_DIR;
|
|
2866
|
-
const builtInPrompt = buildArtifactPrompt(artifactsDir);
|
|
2867
|
-
const libraryPrompt = opts?.library?.promptText?.trim();
|
|
2868
|
-
const customPrompt = resolveSystemPrompt(systemPrompt);
|
|
2869
|
-
const sys = [builtInPrompt, libraryPrompt, customPrompt].filter(Boolean).join("\n\n");
|
|
2870
|
-
const includeTaskPrompt = opts?.includeTaskPrompt !== false;
|
|
2871
|
-
const prompt = includeTaskPrompt && !opts?.resume ? buildPrompt(task) : "";
|
|
2872
|
-
const sessionId = agent === "claude" && includeTaskPrompt ? executorSessionId(task.id, task.runId) : void 0;
|
|
2873
|
-
return boardEnvPrefix(task, opts) + assembleCommand(
|
|
2874
|
-
agent,
|
|
2875
|
-
sys,
|
|
2876
|
-
prompt,
|
|
2877
|
-
model,
|
|
2878
|
-
task.effort,
|
|
2879
|
-
opts,
|
|
2880
|
-
task.worktreePath,
|
|
2881
|
-
sessionId,
|
|
2882
|
-
workspacePath
|
|
2883
|
-
);
|
|
2884
|
-
}
|
|
2885
|
-
|
|
2886
2942
|
// packages/core/src/git.ts
|
|
2943
|
+
init_launch();
|
|
2887
2944
|
init_env();
|
|
2888
2945
|
|
|
2889
2946
|
// packages/core/src/github.ts
|
|
@@ -4142,10 +4199,10 @@ function taskAttachmentStagingPath(workspacePath, taskId) {
|
|
|
4142
4199
|
|
|
4143
4200
|
// packages/core/src/agents.ts
|
|
4144
4201
|
init_env();
|
|
4202
|
+
init_effort();
|
|
4145
4203
|
import { execFile as execFile6 } from "child_process";
|
|
4146
4204
|
import { promisify as promisify3 } from "util";
|
|
4147
4205
|
var pexec3 = promisify3(execFile6);
|
|
4148
|
-
var AGENT_EFFORTS = ["low", "medium", "high", "xhigh"];
|
|
4149
4206
|
var DEFAULT_TASK_EFFORT = "medium";
|
|
4150
4207
|
var AGENT_CLIS = [
|
|
4151
4208
|
{
|
|
@@ -4535,8 +4592,8 @@ async function taskCommand(argv) {
|
|
|
4535
4592
|
}
|
|
4536
4593
|
|
|
4537
4594
|
// packages/cli/src/host.ts
|
|
4538
|
-
import
|
|
4539
|
-
import
|
|
4595
|
+
import fs20 from "fs";
|
|
4596
|
+
import path24 from "path";
|
|
4540
4597
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
4541
4598
|
|
|
4542
4599
|
// packages/core/src/events.ts
|
|
@@ -5074,35 +5131,266 @@ async function syncBuiltinsAtStartup() {
|
|
|
5074
5131
|
}
|
|
5075
5132
|
|
|
5076
5133
|
// packages/core/src/service.ts
|
|
5077
|
-
import
|
|
5078
|
-
import
|
|
5079
|
-
import
|
|
5134
|
+
import fs18 from "fs";
|
|
5135
|
+
import os8 from "os";
|
|
5136
|
+
import path22 from "path";
|
|
5080
5137
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
5081
5138
|
|
|
5082
5139
|
// packages/core/src/agent-models.ts
|
|
5083
|
-
import
|
|
5140
|
+
import fs13 from "fs";
|
|
5141
|
+
import os6 from "os";
|
|
5142
|
+
import path17 from "path";
|
|
5143
|
+
|
|
5144
|
+
// packages/core/src/claude-models.ts
|
|
5145
|
+
init_effort();
|
|
5084
5146
|
import fs12 from "fs";
|
|
5085
5147
|
import os4 from "os";
|
|
5086
5148
|
import path16 from "path";
|
|
5087
|
-
|
|
5149
|
+
|
|
5150
|
+
// packages/core/src/json-lines-process.ts
|
|
5088
5151
|
init_env();
|
|
5089
|
-
|
|
5152
|
+
import { spawn as spawn4 } from "child_process";
|
|
5153
|
+
function spawnJsonLines(bin, args, opts) {
|
|
5154
|
+
const spawnOpts = { cwd: opts.cwd, env: opts.env ?? execEnv(), windowsHide: true };
|
|
5155
|
+
const proc = process.platform === "win32" ? spawn4([bin, ...args].join(" "), { ...spawnOpts, shell: true }) : spawn4(bin, args, spawnOpts);
|
|
5156
|
+
const inbox = [];
|
|
5157
|
+
const waiters = [];
|
|
5158
|
+
let failure = null;
|
|
5159
|
+
let stderr = "";
|
|
5160
|
+
let exited = false;
|
|
5161
|
+
const finished = () => {
|
|
5162
|
+
if (exited) return;
|
|
5163
|
+
exited = true;
|
|
5164
|
+
opts.onExit?.();
|
|
5165
|
+
};
|
|
5166
|
+
const fail2 = (err) => {
|
|
5167
|
+
if (failure) return;
|
|
5168
|
+
failure = err;
|
|
5169
|
+
for (const w of waiters.splice(0)) w.reject(err);
|
|
5170
|
+
};
|
|
5171
|
+
const deliver = (message) => {
|
|
5172
|
+
const i = waiters.findIndex((w) => w.match(message));
|
|
5173
|
+
if (i === -1) inbox.push(message);
|
|
5174
|
+
else waiters.splice(i, 1)[0].resolve(message);
|
|
5175
|
+
};
|
|
5176
|
+
let buffered = "";
|
|
5177
|
+
proc.stdout.setEncoding("utf8");
|
|
5178
|
+
proc.stdout.on("data", (chunk) => {
|
|
5179
|
+
buffered += chunk;
|
|
5180
|
+
let nl;
|
|
5181
|
+
while ((nl = buffered.indexOf("\n")) >= 0) {
|
|
5182
|
+
const line = buffered.slice(0, nl).trim();
|
|
5183
|
+
buffered = buffered.slice(nl + 1);
|
|
5184
|
+
if (!line) continue;
|
|
5185
|
+
let message;
|
|
5186
|
+
try {
|
|
5187
|
+
message = JSON.parse(line);
|
|
5188
|
+
} catch {
|
|
5189
|
+
continue;
|
|
5190
|
+
}
|
|
5191
|
+
deliver(message);
|
|
5192
|
+
}
|
|
5193
|
+
});
|
|
5194
|
+
proc.stderr.setEncoding("utf8");
|
|
5195
|
+
proc.stderr.on("data", (chunk) => {
|
|
5196
|
+
stderr = (stderr + chunk).slice(-2048);
|
|
5197
|
+
});
|
|
5198
|
+
proc.on("error", (err) => {
|
|
5199
|
+
fail2(err);
|
|
5200
|
+
if (proc.pid === void 0) {
|
|
5201
|
+
clearTimeout(timer);
|
|
5202
|
+
finished();
|
|
5203
|
+
}
|
|
5204
|
+
});
|
|
5205
|
+
proc.on("exit", (code2, signal) => {
|
|
5206
|
+
finished();
|
|
5207
|
+
clearTimeout(timer);
|
|
5208
|
+
clearTimeout(graceTimer);
|
|
5209
|
+
const reason = signal ? `was killed (${signal})` : `exited with code ${code2}`;
|
|
5210
|
+
fail2(new Error(`${bin} ${reason}${stderr.trim() ? `: ${stderr.trim()}` : ""}`));
|
|
5211
|
+
});
|
|
5212
|
+
proc.stdin.on("error", () => {
|
|
5213
|
+
});
|
|
5214
|
+
const kill = () => {
|
|
5215
|
+
if (exited) return;
|
|
5216
|
+
if (process.platform === "win32" && proc.pid !== void 0) {
|
|
5217
|
+
spawn4("taskkill", ["/pid", String(proc.pid), "/T", "/F"], { windowsHide: true }).on("error", () => proc.kill());
|
|
5218
|
+
return;
|
|
5219
|
+
}
|
|
5220
|
+
proc.kill();
|
|
5221
|
+
};
|
|
5222
|
+
const timer = setTimeout(() => {
|
|
5223
|
+
fail2(new Error(`${bin} did not answer within ${opts.timeoutMs}ms`));
|
|
5224
|
+
kill();
|
|
5225
|
+
}, opts.timeoutMs);
|
|
5226
|
+
let graceTimer;
|
|
5227
|
+
return {
|
|
5228
|
+
send(message) {
|
|
5229
|
+
if (!failure) proc.stdin.write(`${JSON.stringify(message)}
|
|
5230
|
+
`);
|
|
5231
|
+
},
|
|
5232
|
+
next(match) {
|
|
5233
|
+
const i = inbox.findIndex(match);
|
|
5234
|
+
if (i >= 0) return Promise.resolve(inbox.splice(i, 1)[0]);
|
|
5235
|
+
if (failure) return Promise.reject(failure);
|
|
5236
|
+
return new Promise((resolve, reject) => waiters.push({ match, resolve, reject }));
|
|
5237
|
+
},
|
|
5238
|
+
close() {
|
|
5239
|
+
clearTimeout(timer);
|
|
5240
|
+
fail2(new Error(`${bin} conversation closed`));
|
|
5241
|
+
proc.stdin.end();
|
|
5242
|
+
if (!exited && !graceTimer) graceTimer = setTimeout(kill, opts.exitGraceMs ?? 3e3);
|
|
5243
|
+
}
|
|
5244
|
+
};
|
|
5245
|
+
}
|
|
5246
|
+
|
|
5247
|
+
// packages/core/src/claude-models.ts
|
|
5248
|
+
var CLAUDE_MODELS_TIMEOUT_MS = 15e3;
|
|
5249
|
+
var REQUEST_ID = "vibeflow-models";
|
|
5250
|
+
var CLAUDE_ARGS = ["-p", "--input-format", "stream-json", "--output-format", "stream-json", "--verbose"];
|
|
5251
|
+
function openClaude() {
|
|
5252
|
+
const cwd = fs12.mkdtempSync(path16.join(os4.tmpdir(), "vf-claude-models-"));
|
|
5253
|
+
return spawnJsonLines("claude", CLAUDE_ARGS, {
|
|
5254
|
+
cwd,
|
|
5255
|
+
timeoutMs: CLAUDE_MODELS_TIMEOUT_MS,
|
|
5256
|
+
onExit: () => fs12.rmSync(cwd, { recursive: true, force: true })
|
|
5257
|
+
});
|
|
5258
|
+
}
|
|
5259
|
+
function isRecord(value) {
|
|
5260
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5261
|
+
}
|
|
5262
|
+
function isOurResponse(message) {
|
|
5263
|
+
return isRecord(message) && message.type === "control_response" && isRecord(message.response) && message.response.request_id === REQUEST_ID;
|
|
5264
|
+
}
|
|
5265
|
+
function parseClaudeInitialize(message) {
|
|
5266
|
+
const outer = isRecord(message) && isRecord(message.response) ? message.response : null;
|
|
5267
|
+
if (outer?.subtype === "error") throw new Error(`claude refused the handshake: ${String(outer.error ?? "unknown error")}`);
|
|
5268
|
+
const body = outer && isRecord(outer.response) ? outer.response : null;
|
|
5269
|
+
if (!body || !Array.isArray(body.models)) throw new Error("claude handshake has no models array");
|
|
5270
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5271
|
+
const models = [];
|
|
5272
|
+
for (const entry of body.models) {
|
|
5273
|
+
if (!isRecord(entry) || typeof entry.value !== "string") continue;
|
|
5274
|
+
const id = entry.value.trim();
|
|
5275
|
+
if (!id || id === "default" || seen.has(id)) continue;
|
|
5276
|
+
seen.add(id);
|
|
5277
|
+
const label = typeof entry.displayName === "string" && entry.displayName.trim() ? entry.displayName.trim() : id;
|
|
5278
|
+
const description = typeof entry.description === "string" && entry.description.trim() ? entry.description.trim() : void 0;
|
|
5279
|
+
const levels = entry.supportsEffort === true && Array.isArray(entry.supportedEffortLevels) ? entry.supportedEffortLevels.filter(isAgentEffort) : [];
|
|
5280
|
+
models.push({ id, label, ...description ? { description } : {}, efforts: levels });
|
|
5281
|
+
}
|
|
5282
|
+
if (models.length === 0) throw new Error("claude handshake lists no selectable models");
|
|
5283
|
+
return models;
|
|
5284
|
+
}
|
|
5285
|
+
async function fetchClaudeModels(open = openClaude) {
|
|
5286
|
+
const channel = open();
|
|
5287
|
+
try {
|
|
5288
|
+
channel.send({ type: "control_request", request_id: REQUEST_ID, request: { subtype: "initialize" } });
|
|
5289
|
+
return parseClaudeInitialize(await channel.next(isOurResponse));
|
|
5290
|
+
} finally {
|
|
5291
|
+
channel.close();
|
|
5292
|
+
}
|
|
5293
|
+
}
|
|
5294
|
+
|
|
5295
|
+
// packages/core/src/codex-models.ts
|
|
5296
|
+
init_effort();
|
|
5297
|
+
init_env();
|
|
5298
|
+
import os5 from "os";
|
|
5090
5299
|
var CODEX_MODELS_TIMEOUT_MS = 2e4;
|
|
5300
|
+
var MAX_PAGES = 20;
|
|
5301
|
+
var CodexLoginRequiredError = class extends Error {
|
|
5302
|
+
constructor() {
|
|
5303
|
+
super("Codex CLI is not signed in; run `codex login`");
|
|
5304
|
+
this.name = "CodexLoginRequiredError";
|
|
5305
|
+
}
|
|
5306
|
+
};
|
|
5307
|
+
function codexAppServerCommand() {
|
|
5308
|
+
return {
|
|
5309
|
+
bin: "codex",
|
|
5310
|
+
args: ["app-server", "--listen", "stdio://"],
|
|
5311
|
+
cwd: os5.homedir(),
|
|
5312
|
+
env: execEnv()
|
|
5313
|
+
};
|
|
5314
|
+
}
|
|
5315
|
+
function openCodex() {
|
|
5316
|
+
const { bin, args, cwd, env } = codexAppServerCommand();
|
|
5317
|
+
return spawnJsonLines(bin, args, { cwd, env, timeoutMs: CODEX_MODELS_TIMEOUT_MS });
|
|
5318
|
+
}
|
|
5319
|
+
function isRecord2(value) {
|
|
5320
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
5321
|
+
}
|
|
5322
|
+
var responseTo = (id) => (message) => isRecord2(message) && message.id === id && ("result" in message || "error" in message);
|
|
5323
|
+
function resultOf(message, method) {
|
|
5324
|
+
if (!isRecord2(message)) throw new Error(`codex ${method}: malformed response`);
|
|
5325
|
+
if (isRecord2(message.error)) throw new Error(`codex ${method}: ${String(message.error.message ?? "request failed")}`);
|
|
5326
|
+
if (!isRecord2(message.result)) throw new Error(`codex ${method}: response has no result`);
|
|
5327
|
+
return message.result;
|
|
5328
|
+
}
|
|
5329
|
+
function isCodexSignedIn(result) {
|
|
5330
|
+
return result.account != null || result.requiresOpenaiAuth === false;
|
|
5331
|
+
}
|
|
5332
|
+
function parseCodexModelPage(result) {
|
|
5333
|
+
if (!Array.isArray(result.data)) throw new Error("codex model/list has no data array");
|
|
5334
|
+
const models = [];
|
|
5335
|
+
for (const entry of result.data) {
|
|
5336
|
+
if (!isRecord2(entry) || typeof entry.id !== "string" || !entry.id.trim() || entry.hidden === true) continue;
|
|
5337
|
+
const id = entry.id.trim();
|
|
5338
|
+
const label = typeof entry.displayName === "string" && entry.displayName.trim() ? entry.displayName.trim() : id;
|
|
5339
|
+
const description = typeof entry.description === "string" && entry.description.trim() ? entry.description.trim() : void 0;
|
|
5340
|
+
const efforts = Array.isArray(entry.supportedReasoningEfforts) ? entry.supportedReasoningEfforts.map((e) => isRecord2(e) ? e.reasoningEffort : void 0).filter(isAgentEffort) : void 0;
|
|
5341
|
+
models.push({ id, label, ...description ? { description } : {}, ...efforts ? { efforts } : {} });
|
|
5342
|
+
}
|
|
5343
|
+
const nextCursor = typeof result.nextCursor === "string" && result.nextCursor ? result.nextCursor : null;
|
|
5344
|
+
return { models, nextCursor };
|
|
5345
|
+
}
|
|
5346
|
+
async function fetchCodexModels(clientVersion, open = openCodex) {
|
|
5347
|
+
const channel = open();
|
|
5348
|
+
try {
|
|
5349
|
+
channel.send({
|
|
5350
|
+
method: "initialize",
|
|
5351
|
+
id: 1,
|
|
5352
|
+
params: { clientInfo: { name: "vibeflow", title: "VibeFlow", version: clientVersion } }
|
|
5353
|
+
});
|
|
5354
|
+
resultOf(await channel.next(responseTo(1)), "initialize");
|
|
5355
|
+
channel.send({ method: "initialized", params: {} });
|
|
5356
|
+
channel.send({ method: "account/read", id: 2, params: { refreshToken: false } });
|
|
5357
|
+
if (!isCodexSignedIn(resultOf(await channel.next(responseTo(2)), "account/read"))) {
|
|
5358
|
+
throw new CodexLoginRequiredError();
|
|
5359
|
+
}
|
|
5360
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5361
|
+
const models = [];
|
|
5362
|
+
let cursor = null;
|
|
5363
|
+
for (let page = 0; page < MAX_PAGES; page++) {
|
|
5364
|
+
const id = 3 + page;
|
|
5365
|
+
channel.send({
|
|
5366
|
+
method: "model/list",
|
|
5367
|
+
id,
|
|
5368
|
+
params: { limit: 100, includeHidden: false, ...cursor ? { cursor } : {} }
|
|
5369
|
+
});
|
|
5370
|
+
const parsed = parseCodexModelPage(resultOf(await channel.next(responseTo(id)), "model/list"));
|
|
5371
|
+
for (const model of parsed.models) {
|
|
5372
|
+
if (seen.has(model.id)) continue;
|
|
5373
|
+
seen.add(model.id);
|
|
5374
|
+
models.push(model);
|
|
5375
|
+
}
|
|
5376
|
+
cursor = parsed.nextCursor;
|
|
5377
|
+
if (!cursor) break;
|
|
5378
|
+
}
|
|
5379
|
+
if (cursor) throw new Error(`codex model/list did not finish within ${MAX_PAGES} pages`);
|
|
5380
|
+
if (models.length === 0) throw new Error("codex model/list lists no selectable models");
|
|
5381
|
+
return models;
|
|
5382
|
+
} finally {
|
|
5383
|
+
channel.close();
|
|
5384
|
+
}
|
|
5385
|
+
}
|
|
5386
|
+
|
|
5387
|
+
// packages/core/src/agent-models.ts
|
|
5091
5388
|
function builtinModels(id) {
|
|
5092
5389
|
return (AGENT_CLIS.find((a) => a.id === id) ?? AGENT_CLIS[0]).models;
|
|
5093
5390
|
}
|
|
5094
5391
|
function defaultCodexHome() {
|
|
5095
5392
|
const configured = process.env.CODEX_HOME?.trim();
|
|
5096
|
-
return configured ? configured :
|
|
5097
|
-
}
|
|
5098
|
-
async function defaultRunCodexModels() {
|
|
5099
|
-
const { stdout } = await pexec4("codex", ["debug", "models"], {
|
|
5100
|
-
env: execEnv(),
|
|
5101
|
-
timeout: CODEX_MODELS_TIMEOUT_MS,
|
|
5102
|
-
maxBuffer: 16 * 1024 * 1024,
|
|
5103
|
-
windowsHide: true
|
|
5104
|
-
});
|
|
5105
|
-
return stdout;
|
|
5393
|
+
return configured ? configured : path17.join(os6.homedir(), ".codex");
|
|
5106
5394
|
}
|
|
5107
5395
|
function parseCodexCatalog(raw) {
|
|
5108
5396
|
const body = JSON.parse(raw);
|
|
@@ -5127,36 +5415,75 @@ function parseCodexCatalog(raw) {
|
|
|
5127
5415
|
function errorMessage(err) {
|
|
5128
5416
|
return err instanceof Error ? err.message : String(err);
|
|
5129
5417
|
}
|
|
5130
|
-
async function
|
|
5131
|
-
|
|
5132
|
-
if (opts.refresh) {
|
|
5133
|
-
try {
|
|
5134
|
-
const stdout = await (opts.runCodexModels ?? defaultRunCodexModels)();
|
|
5135
|
-
return { models: parseCodexCatalog(stdout), source: "codex-cli" };
|
|
5136
|
-
} catch (err) {
|
|
5137
|
-
return { models: builtinModels(id), source: "builtin", error: errorMessage(err) };
|
|
5138
|
-
}
|
|
5139
|
-
}
|
|
5140
|
-
const cachePath = path16.join(opts.codexHome ?? defaultCodexHome(), "models_cache.json");
|
|
5418
|
+
async function readCodexCache(codexHome) {
|
|
5419
|
+
const cachePath = path17.join(codexHome, "models_cache.json");
|
|
5141
5420
|
try {
|
|
5142
|
-
const raw = await
|
|
5421
|
+
const raw = await fs13.promises.readFile(cachePath, "utf8");
|
|
5143
5422
|
return { models: parseCodexCatalog(raw), source: "codex-cache" };
|
|
5144
5423
|
} catch (err) {
|
|
5145
5424
|
const missing = err.code === "ENOENT";
|
|
5146
5425
|
return {
|
|
5147
|
-
models: builtinModels(
|
|
5426
|
+
models: builtinModels("codex"),
|
|
5148
5427
|
source: "builtin",
|
|
5149
5428
|
...missing ? {} : { error: errorMessage(err) }
|
|
5150
5429
|
};
|
|
5151
5430
|
}
|
|
5152
5431
|
}
|
|
5432
|
+
function createModelCatalog(opts) {
|
|
5433
|
+
const fetchClaude = opts.fetchClaude ?? (() => fetchClaudeModels());
|
|
5434
|
+
const fetchCodex = opts.fetchCodex ?? (() => fetchCodexModels(opts.version));
|
|
5435
|
+
const inFlight = /* @__PURE__ */ new Map();
|
|
5436
|
+
const settled = /* @__PURE__ */ new Map();
|
|
5437
|
+
const fallback = (id) => id === "codex" ? readCodexCache(opts.codexHome ?? defaultCodexHome()) : Promise.resolve({ models: builtinModels(id), source: "builtin" });
|
|
5438
|
+
const ask = async (id) => {
|
|
5439
|
+
try {
|
|
5440
|
+
const models = await (id === "codex" ? fetchCodex() : fetchClaude());
|
|
5441
|
+
return { models, source: id === "codex" ? "codex-app-server" : "claude-cli" };
|
|
5442
|
+
} catch (err) {
|
|
5443
|
+
const list = await fallback(id);
|
|
5444
|
+
return {
|
|
5445
|
+
...list,
|
|
5446
|
+
error: errorMessage(err),
|
|
5447
|
+
...err instanceof CodexLoginRequiredError ? { loginRequired: true } : {}
|
|
5448
|
+
};
|
|
5449
|
+
}
|
|
5450
|
+
};
|
|
5451
|
+
const refresh = (id) => {
|
|
5452
|
+
const running = inFlight.get(id);
|
|
5453
|
+
if (running) return running;
|
|
5454
|
+
const pending = ask(id).then((list) => {
|
|
5455
|
+
settled.set(id, list);
|
|
5456
|
+
return list;
|
|
5457
|
+
}).finally(() => inFlight.delete(id));
|
|
5458
|
+
inFlight.set(id, pending);
|
|
5459
|
+
return pending;
|
|
5460
|
+
};
|
|
5461
|
+
return {
|
|
5462
|
+
async get(id) {
|
|
5463
|
+
const answer = settled.get(id);
|
|
5464
|
+
if (answer) return answer;
|
|
5465
|
+
const list = await fallback(id);
|
|
5466
|
+
return inFlight.has(id) ? { ...list, pending: true } : list;
|
|
5467
|
+
},
|
|
5468
|
+
refresh,
|
|
5469
|
+
warm() {
|
|
5470
|
+
for (const agent of AGENT_CLIS) void refresh(agent.id);
|
|
5471
|
+
},
|
|
5472
|
+
findModel(id, modelId) {
|
|
5473
|
+
return settled.get(id)?.models.find((model) => model.id === modelId);
|
|
5474
|
+
}
|
|
5475
|
+
};
|
|
5476
|
+
}
|
|
5477
|
+
|
|
5478
|
+
// packages/core/src/service.ts
|
|
5479
|
+
init_effort();
|
|
5153
5480
|
|
|
5154
5481
|
// packages/core/src/github-auth.ts
|
|
5155
5482
|
init_env();
|
|
5156
|
-
import { execFile as
|
|
5157
|
-
import { promisify as
|
|
5483
|
+
import { execFile as execFile7 } from "child_process";
|
|
5484
|
+
import { promisify as promisify4 } from "util";
|
|
5158
5485
|
import * as nodePty3 from "node-pty";
|
|
5159
|
-
var
|
|
5486
|
+
var pexec4 = promisify4(execFile7);
|
|
5160
5487
|
var GITHUB_HOST = "github.com";
|
|
5161
5488
|
var DEVICE_URL = "https://github.com/login/device";
|
|
5162
5489
|
var activeLogin = null;
|
|
@@ -5191,7 +5518,7 @@ function errorMessage2(err) {
|
|
|
5191
5518
|
}
|
|
5192
5519
|
async function getGitHubCliAuthStatus() {
|
|
5193
5520
|
try {
|
|
5194
|
-
const { stdout } = await
|
|
5521
|
+
const { stdout } = await pexec4(
|
|
5195
5522
|
"gh",
|
|
5196
5523
|
["auth", "status", "--hostname", GITHUB_HOST, "--json", "hosts"],
|
|
5197
5524
|
{
|
|
@@ -5223,7 +5550,7 @@ async function logoutGitHubCli() {
|
|
|
5223
5550
|
const status = await getGitHubCliAuthStatus();
|
|
5224
5551
|
if (!status.installed) throw new Error("\u627E\u4E0D\u5230 GitHub CLI\uFF08gh\uFF09\u3002");
|
|
5225
5552
|
if (!status.login) return status;
|
|
5226
|
-
await
|
|
5553
|
+
await pexec4(
|
|
5227
5554
|
"gh",
|
|
5228
5555
|
["auth", "logout", "--hostname", GITHUB_HOST, "--user", status.login],
|
|
5229
5556
|
{
|
|
@@ -5306,45 +5633,45 @@ function startGitHubCliLogin(onEvent) {
|
|
|
5306
5633
|
}
|
|
5307
5634
|
|
|
5308
5635
|
// packages/core/src/board-cli.ts
|
|
5309
|
-
import
|
|
5310
|
-
import
|
|
5636
|
+
import fs14 from "fs";
|
|
5637
|
+
import path18 from "path";
|
|
5311
5638
|
import { pathToFileURL } from "url";
|
|
5312
5639
|
async function boardCliLaunchInfo() {
|
|
5313
|
-
const storeDir =
|
|
5640
|
+
const storeDir = path18.dirname(getStorePath());
|
|
5314
5641
|
const entry = getPlatform().cliEntry?.();
|
|
5315
|
-
if (entry &&
|
|
5642
|
+
if (entry && fs14.existsSync(entry)) return { storeDir, cliPath: entry };
|
|
5316
5643
|
const root = getPlatform().sourceRoot();
|
|
5317
5644
|
if (!root) return { storeDir };
|
|
5318
|
-
const cliPath =
|
|
5319
|
-
const loaderPath =
|
|
5320
|
-
if (!
|
|
5645
|
+
const cliPath = path18.join(root, "scripts", "vibeflow.mjs");
|
|
5646
|
+
const loaderPath = path18.join(root, "test", "support", "register.mjs");
|
|
5647
|
+
if (!fs14.existsSync(cliPath) || !fs14.existsSync(loaderPath)) return { storeDir };
|
|
5321
5648
|
return { storeDir, cliPath, loaderPath: pathToFileURL(loaderPath).href };
|
|
5322
5649
|
}
|
|
5323
5650
|
|
|
5324
5651
|
// packages/core/src/decisions.ts
|
|
5325
|
-
import
|
|
5326
|
-
import
|
|
5652
|
+
import fs15 from "fs";
|
|
5653
|
+
import path19 from "path";
|
|
5327
5654
|
var DECISIONS_FILE_SUFFIX = ".DECISIONS.md";
|
|
5328
5655
|
function decisionsKey(worktreePath, branch) {
|
|
5329
|
-
return worktreePath ?
|
|
5656
|
+
return worktreePath ? path19.basename(worktreePath) : branch.replace(/\//g, "-");
|
|
5330
5657
|
}
|
|
5331
5658
|
function decisionsPath(workspacePath, key) {
|
|
5332
|
-
return
|
|
5659
|
+
return path19.join(workspacePath, `${key}${DECISIONS_FILE_SUFFIX}`);
|
|
5333
5660
|
}
|
|
5334
5661
|
function deleteDecisions(workspacePath, key) {
|
|
5335
5662
|
try {
|
|
5336
|
-
|
|
5663
|
+
fs15.rmSync(decisionsPath(workspacePath, key), { force: true });
|
|
5337
5664
|
} catch {
|
|
5338
5665
|
}
|
|
5339
5666
|
}
|
|
5340
5667
|
|
|
5341
5668
|
// packages/core/src/specs.ts
|
|
5342
|
-
import
|
|
5343
|
-
import
|
|
5669
|
+
import fs16 from "fs";
|
|
5670
|
+
import path20 from "path";
|
|
5344
5671
|
var SPEC_FILE_NAME = "spec.md";
|
|
5345
5672
|
var MAX_SPEC_BYTES = 256 * 1024;
|
|
5346
5673
|
function isSpecPath(p) {
|
|
5347
|
-
return
|
|
5674
|
+
return path20.posix.basename(p) === SPEC_FILE_NAME;
|
|
5348
5675
|
}
|
|
5349
5676
|
function clip2(buf) {
|
|
5350
5677
|
return {
|
|
@@ -5359,10 +5686,10 @@ async function readWorktreeSpecs(worktreePath, baseBranch) {
|
|
|
5359
5686
|
for (const entry of entries) {
|
|
5360
5687
|
if (entry.status === "D" || !isSpecPath(entry.path)) continue;
|
|
5361
5688
|
try {
|
|
5362
|
-
const full =
|
|
5363
|
-
const stat =
|
|
5689
|
+
const full = path20.join(worktreePath, entry.path);
|
|
5690
|
+
const stat = fs16.statSync(full);
|
|
5364
5691
|
if (!stat.isFile()) continue;
|
|
5365
|
-
specs.push({ path: entry.path, ...clip2(
|
|
5692
|
+
specs.push({ path: entry.path, ...clip2(fs16.readFileSync(full)), updatedAt: stat.mtimeMs });
|
|
5366
5693
|
} catch {
|
|
5367
5694
|
}
|
|
5368
5695
|
}
|
|
@@ -5427,7 +5754,7 @@ async function readSpecsAt(cwd, ref, paths) {
|
|
|
5427
5754
|
// packages/core/src/chat-session.ts
|
|
5428
5755
|
init_env();
|
|
5429
5756
|
init_git_bash();
|
|
5430
|
-
import { spawn as
|
|
5757
|
+
import { spawn as spawn6 } from "child_process";
|
|
5431
5758
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
5432
5759
|
|
|
5433
5760
|
// packages/core/src/chat-store.ts
|
|
@@ -5583,7 +5910,7 @@ ${attachmentLines.join("\n")}` : text;
|
|
|
5583
5910
|
`--append-system-prompt ${quote(systemPrompt)}`,
|
|
5584
5911
|
quote(fullText)
|
|
5585
5912
|
].filter(Boolean).join(" ") : buildRawAgentCommand(agentCli, model, fullText, systemPrompt, worktreePath, quote, workspacePath);
|
|
5586
|
-
const proc =
|
|
5913
|
+
const proc = spawn6(shell.command, shell.args(cmd), {
|
|
5587
5914
|
cwd: worktreePath,
|
|
5588
5915
|
env: execEnv()
|
|
5589
5916
|
});
|
|
@@ -5709,20 +6036,22 @@ function startChatSend(opts, sender) {
|
|
|
5709
6036
|
|
|
5710
6037
|
// packages/core/src/service.ts
|
|
5711
6038
|
init_attachments();
|
|
6039
|
+
init_launch();
|
|
5712
6040
|
|
|
5713
6041
|
// packages/core/src/progress-tracker.ts
|
|
5714
6042
|
init_progress();
|
|
5715
|
-
|
|
5716
|
-
import
|
|
5717
|
-
import
|
|
6043
|
+
init_launch();
|
|
6044
|
+
import fs17 from "fs";
|
|
6045
|
+
import os7 from "os";
|
|
6046
|
+
import path21 from "path";
|
|
5718
6047
|
var SINCE_SLACK_MS = 5e3;
|
|
5719
6048
|
function claudeProjectDir(home, cwd) {
|
|
5720
|
-
return
|
|
6049
|
+
return path21.join(home, ".claude", "projects", cwd.replace(/[^a-zA-Z0-9]/g, "-"));
|
|
5721
6050
|
}
|
|
5722
6051
|
function pathAliases(p) {
|
|
5723
|
-
const resolved =
|
|
6052
|
+
const resolved = path21.resolve(p);
|
|
5724
6053
|
try {
|
|
5725
|
-
const real =
|
|
6054
|
+
const real = fs17.realpathSync(resolved);
|
|
5726
6055
|
return real === resolved ? [resolved] : [resolved, real];
|
|
5727
6056
|
} catch {
|
|
5728
6057
|
return [resolved];
|
|
@@ -5738,7 +6067,7 @@ function samePath(a, b) {
|
|
|
5738
6067
|
}
|
|
5739
6068
|
function listFiles(dir, test) {
|
|
5740
6069
|
try {
|
|
5741
|
-
return
|
|
6070
|
+
return fs17.readdirSync(dir).filter(test).map((name) => path21.join(dir, name));
|
|
5742
6071
|
} catch {
|
|
5743
6072
|
return [];
|
|
5744
6073
|
}
|
|
@@ -5746,22 +6075,22 @@ function listFiles(dir, test) {
|
|
|
5746
6075
|
function firstLine(file) {
|
|
5747
6076
|
let fd;
|
|
5748
6077
|
try {
|
|
5749
|
-
fd =
|
|
6078
|
+
fd = fs17.openSync(file, "r");
|
|
5750
6079
|
const buf = Buffer.alloc(64 * 1024);
|
|
5751
|
-
const n =
|
|
6080
|
+
const n = fs17.readSync(fd, buf, 0, buf.length, 0);
|
|
5752
6081
|
const text = buf.subarray(0, n).toString("utf8");
|
|
5753
6082
|
const nl = text.indexOf("\n");
|
|
5754
6083
|
return nl === -1 ? null : text.slice(0, nl);
|
|
5755
6084
|
} catch {
|
|
5756
6085
|
return null;
|
|
5757
6086
|
} finally {
|
|
5758
|
-
if (fd !== void 0)
|
|
6087
|
+
if (fd !== void 0) fs17.closeSync(fd);
|
|
5759
6088
|
}
|
|
5760
6089
|
}
|
|
5761
6090
|
function advance(file, cursor, reduce, initial) {
|
|
5762
6091
|
let size;
|
|
5763
6092
|
try {
|
|
5764
|
-
size =
|
|
6093
|
+
size = fs17.statSync(file).size;
|
|
5765
6094
|
} catch {
|
|
5766
6095
|
return;
|
|
5767
6096
|
}
|
|
@@ -5773,9 +6102,9 @@ function advance(file, cursor, reduce, initial) {
|
|
|
5773
6102
|
if (size === cursor.offset) return;
|
|
5774
6103
|
let fd;
|
|
5775
6104
|
try {
|
|
5776
|
-
fd =
|
|
6105
|
+
fd = fs17.openSync(file, "r");
|
|
5777
6106
|
const chunk = Buffer.alloc(size - cursor.offset);
|
|
5778
|
-
const n =
|
|
6107
|
+
const n = fs17.readSync(fd, chunk, 0, chunk.length, cursor.offset);
|
|
5779
6108
|
cursor.offset += n;
|
|
5780
6109
|
const data = Buffer.concat([cursor.rest, chunk.subarray(0, n)]);
|
|
5781
6110
|
const nl = data.lastIndexOf(10);
|
|
@@ -5789,7 +6118,7 @@ function advance(file, cursor, reduce, initial) {
|
|
|
5789
6118
|
}
|
|
5790
6119
|
} catch {
|
|
5791
6120
|
} finally {
|
|
5792
|
-
if (fd !== void 0)
|
|
6121
|
+
if (fd !== void 0) fs17.closeSync(fd);
|
|
5793
6122
|
}
|
|
5794
6123
|
}
|
|
5795
6124
|
function newCursor(state) {
|
|
@@ -5808,14 +6137,14 @@ function codexDayDirs(sessionsRoot, since) {
|
|
|
5808
6137
|
const cur = new Date(start.getFullYear(), start.getMonth(), start.getDate());
|
|
5809
6138
|
while (cur <= end) {
|
|
5810
6139
|
const pad = (n) => String(n).padStart(2, "0");
|
|
5811
|
-
days.push(
|
|
6140
|
+
days.push(path21.join(sessionsRoot, String(cur.getFullYear()), pad(cur.getMonth() + 1), pad(cur.getDate())));
|
|
5812
6141
|
cur.setDate(cur.getDate() + 1);
|
|
5813
6142
|
}
|
|
5814
6143
|
return days;
|
|
5815
6144
|
}
|
|
5816
6145
|
function createProgressTracker(opts) {
|
|
5817
|
-
const home = opts.homeDir ??
|
|
5818
|
-
const codexHomes = opts.codexHomes ?? (() => [
|
|
6146
|
+
const home = opts.homeDir ?? os7.homedir();
|
|
6147
|
+
const codexHomes = opts.codexHomes ?? (() => [path21.join(home, ".codex")]);
|
|
5819
6148
|
const pollMs = opts.pollMs ?? 2e3;
|
|
5820
6149
|
const entries = /* @__PURE__ */ new Map();
|
|
5821
6150
|
const belongsToRun = (target, startedAt) => target.since == null || startedAt != null && startedAt >= target.since - SINCE_SLACK_MS;
|
|
@@ -5824,7 +6153,7 @@ function createProgressTracker(opts) {
|
|
|
5824
6153
|
const dirs = pathAliases(target.worktreePath).map((p) => claudeProjectDir(home, p));
|
|
5825
6154
|
for (const file of dirs.flatMap((dir) => listFiles(dir, (n) => n.endsWith(".jsonl")))) {
|
|
5826
6155
|
if (entry.claude.has(file) || entry.rejected.has(file)) continue;
|
|
5827
|
-
const session =
|
|
6156
|
+
const session = path21.basename(file, ".jsonl");
|
|
5828
6157
|
if (session !== target.sessionId) {
|
|
5829
6158
|
const head = firstLine(file);
|
|
5830
6159
|
if (head == null) continue;
|
|
@@ -5837,8 +6166,8 @@ function createProgressTracker(opts) {
|
|
|
5837
6166
|
entry.claude.set(file, newCursor(initialClaudeState()));
|
|
5838
6167
|
}
|
|
5839
6168
|
for (const file of Array.from(entry.claude.keys())) {
|
|
5840
|
-
if (file.includes(`${
|
|
5841
|
-
const sub =
|
|
6169
|
+
if (file.includes(`${path21.sep}subagents${path21.sep}`)) continue;
|
|
6170
|
+
const sub = path21.join(file.slice(0, -".jsonl".length), "subagents");
|
|
5842
6171
|
for (const f of listFiles(sub, (n) => n.endsWith(".jsonl"))) {
|
|
5843
6172
|
if (!entry.claude.has(f)) entry.claude.set(f, newCursor(initialClaudeState()));
|
|
5844
6173
|
}
|
|
@@ -5847,7 +6176,7 @@ function createProgressTracker(opts) {
|
|
|
5847
6176
|
function discoverCodex(entry) {
|
|
5848
6177
|
const { target } = entry;
|
|
5849
6178
|
for (const codexHome of codexHomes()) {
|
|
5850
|
-
for (const day of codexDayDirs(
|
|
6179
|
+
for (const day of codexDayDirs(path21.join(codexHome, "sessions"), target.since)) {
|
|
5851
6180
|
for (const file of listFiles(day, (n) => n.startsWith("rollout-") && n.endsWith(".jsonl"))) {
|
|
5852
6181
|
if (entry.codex.has(file) || entry.rejected.has(file)) continue;
|
|
5853
6182
|
const head = firstLine(file);
|
|
@@ -5864,11 +6193,11 @@ function createProgressTracker(opts) {
|
|
|
5864
6193
|
}
|
|
5865
6194
|
}
|
|
5866
6195
|
function drainHooks(entry) {
|
|
5867
|
-
const dir =
|
|
6196
|
+
const dir = path21.join(entry.target.worktreePath, PROGRESS_EVENTS_DIR);
|
|
5868
6197
|
for (const file of listFiles(dir, (n) => n.endsWith(".json")).sort()) {
|
|
5869
6198
|
try {
|
|
5870
|
-
const at =
|
|
5871
|
-
const event = JSON.parse(
|
|
6199
|
+
const at = fs17.statSync(file).mtimeMs;
|
|
6200
|
+
const event = JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
5872
6201
|
const foreign = entry.target.sessionId && typeof event.session_id === "string" && event.session_id !== entry.target.sessionId;
|
|
5873
6202
|
const activity = foreign ? null : activityFromHook(event, at);
|
|
5874
6203
|
if (activity) entry.hookActivity = latestActivity(entry.hookActivity ?? activity, activity);
|
|
@@ -5876,7 +6205,7 @@ function createProgressTracker(opts) {
|
|
|
5876
6205
|
continue;
|
|
5877
6206
|
}
|
|
5878
6207
|
try {
|
|
5879
|
-
|
|
6208
|
+
fs17.unlinkSync(file);
|
|
5880
6209
|
} catch {
|
|
5881
6210
|
}
|
|
5882
6211
|
}
|
|
@@ -5888,8 +6217,8 @@ function createProgressTracker(opts) {
|
|
|
5888
6217
|
if (target.agent === "claude") {
|
|
5889
6218
|
for (const [file, cursor] of entry.claude) {
|
|
5890
6219
|
runUsage = addUsage(runUsage, claudeUsageTotal(cursor.state));
|
|
5891
|
-
if (file.includes(`${
|
|
5892
|
-
const isExecutor =
|
|
6220
|
+
if (file.includes(`${path21.sep}subagents${path21.sep}`)) continue;
|
|
6221
|
+
const isExecutor = path21.basename(file, ".jsonl") === target.sessionId;
|
|
5893
6222
|
const s = cursor.state;
|
|
5894
6223
|
if (isExecutor || !target.sessionId && (!primary || (s.startedAt ?? 0) > (primary.startedAt ?? 0))) {
|
|
5895
6224
|
primary = { todos: s.todos, contextTokens: s.contextTokens, activity: s.activity, startedAt: s.startedAt };
|
|
@@ -5968,10 +6297,10 @@ function createProgressTracker(opts) {
|
|
|
5968
6297
|
};
|
|
5969
6298
|
entry.timer.unref?.();
|
|
5970
6299
|
entries.set(target.taskId, entry);
|
|
5971
|
-
const eventsDir =
|
|
6300
|
+
const eventsDir = path21.join(target.worktreePath, PROGRESS_EVENTS_DIR);
|
|
5972
6301
|
try {
|
|
5973
|
-
|
|
5974
|
-
entry.watcher =
|
|
6302
|
+
fs17.mkdirSync(eventsDir, { recursive: true });
|
|
6303
|
+
entry.watcher = fs17.watch(eventsDir, () => {
|
|
5975
6304
|
if (entry.pending) return;
|
|
5976
6305
|
entry.pending = setTimeout(() => {
|
|
5977
6306
|
entry.pending = null;
|
|
@@ -6056,17 +6385,18 @@ function validBoard(value) {
|
|
|
6056
6385
|
}
|
|
6057
6386
|
return next;
|
|
6058
6387
|
}
|
|
6059
|
-
function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatus }) {
|
|
6388
|
+
function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatus, modelCatalog }) {
|
|
6060
6389
|
const sink = bus.sink();
|
|
6390
|
+
const models = modelCatalog ?? createModelCatalog({ version });
|
|
6061
6391
|
let libraryCodexHome = null;
|
|
6062
6392
|
void libraryRoot().then((root) => {
|
|
6063
6393
|
libraryCodexHome = libraryPaths(root).codexHome;
|
|
6064
6394
|
}).catch(() => {
|
|
6065
6395
|
});
|
|
6066
|
-
const home = homeDir ??
|
|
6396
|
+
const home = homeDir ?? os8.homedir();
|
|
6067
6397
|
const progress = createProgressTracker({
|
|
6068
6398
|
homeDir: home,
|
|
6069
|
-
codexHomes: () => [
|
|
6399
|
+
codexHomes: () => [path22.join(home, ".codex"), ...libraryCodexHome ? [libraryCodexHome] : []],
|
|
6070
6400
|
onUpdate: (taskId, value) => bus.emit("progress:update", { taskId, progress: value }),
|
|
6071
6401
|
onNotify: (taskId, list) => {
|
|
6072
6402
|
const prefs = resolveNotificationSettings(getSettings());
|
|
@@ -6130,12 +6460,16 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6130
6460
|
requireTask(key.split(":")[0]);
|
|
6131
6461
|
return key;
|
|
6132
6462
|
}
|
|
6463
|
+
function launchEffort(task) {
|
|
6464
|
+
const model = models.findModel(taskAgent(task), taskModel(task));
|
|
6465
|
+
return model?.efforts ? clampEffort(task.effort, model.efforts) : task.effort;
|
|
6466
|
+
}
|
|
6133
6467
|
async function launchCommand(task, intent) {
|
|
6134
6468
|
const settings = getSettings();
|
|
6135
6469
|
const library = await libraryLaunchInfo(task.worktreePath);
|
|
6136
6470
|
const boardCli = await boardCliLaunchInfo();
|
|
6137
6471
|
return buildAgentCommand(
|
|
6138
|
-
task,
|
|
6472
|
+
{ ...task, effort: launchEffort(task) },
|
|
6139
6473
|
settings.systemPrompt ?? "",
|
|
6140
6474
|
{
|
|
6141
6475
|
resume: intent.resume === true,
|
|
@@ -6149,13 +6483,13 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6149
6483
|
}
|
|
6150
6484
|
function claudeSessionFile(cwd, sessionId) {
|
|
6151
6485
|
const munged = cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
6152
|
-
return
|
|
6486
|
+
return path22.join(os8.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
|
|
6153
6487
|
}
|
|
6154
6488
|
async function removeTaskWorktree(task) {
|
|
6155
6489
|
if (!task.projectPath || !task.worktreePath) return;
|
|
6156
6490
|
const branch = task.branch || fallbackBranchName(task.id);
|
|
6157
6491
|
await removeWorktree(task.projectPath, task.worktreePath);
|
|
6158
|
-
deleteArtifacts(task.workspacePath ??
|
|
6492
|
+
deleteArtifacts(task.workspacePath ?? path22.dirname(task.worktreePath), task.worktreePath);
|
|
6159
6493
|
await deleteBranch(task.projectPath, branch);
|
|
6160
6494
|
}
|
|
6161
6495
|
function columnOf(taskId) {
|
|
@@ -6246,7 +6580,8 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6246
6580
|
"env:detectAgents": () => detectAgents(),
|
|
6247
6581
|
"agents:listModels": (payload) => {
|
|
6248
6582
|
const { agentId: rawId, refresh } = obj(payload, "payload");
|
|
6249
|
-
|
|
6583
|
+
const id = agentId(rawId);
|
|
6584
|
+
return refresh === true ? models.refresh(id) : models.get(id);
|
|
6250
6585
|
},
|
|
6251
6586
|
// A task does not exist yet when a project is picked, so these two are the
|
|
6252
6587
|
// ones that must take a path. They only inspect or `git init` it.
|
|
@@ -6260,8 +6595,8 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6260
6595
|
"projects:listRecent": () => listRecentProjects(getStore()),
|
|
6261
6596
|
// `vibeflow open <path>` while a host runs: only the host writes the store.
|
|
6262
6597
|
"projects:record": (projectPath) => {
|
|
6263
|
-
const p =
|
|
6264
|
-
if (!
|
|
6598
|
+
const p = path22.resolve(str(projectPath, "projectPath"));
|
|
6599
|
+
if (!fs18.existsSync(p) || !fs18.statSync(p).isDirectory()) invalid(`not a directory: ${p}`);
|
|
6265
6600
|
recordRecentProject(getStore(), p);
|
|
6266
6601
|
return listRecentProjects(getStore());
|
|
6267
6602
|
},
|
|
@@ -6312,7 +6647,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6312
6647
|
if (!task.baseBranch) throw new Error("\u4EFB\u52D9\u6C92\u6709\u8A18\u9304 base branch\uFF0C\u7121\u6CD5\u9084\u539F worktree");
|
|
6313
6648
|
await teardownTask(task.id);
|
|
6314
6649
|
await resetWorktreeToBase(task.worktreePath, task.baseBranch);
|
|
6315
|
-
const workspacePath = task.workspacePath ??
|
|
6650
|
+
const workspacePath = task.workspacePath ?? path22.dirname(task.worktreePath);
|
|
6316
6651
|
deleteArtifacts(workspacePath, task.worktreePath);
|
|
6317
6652
|
deleteDecisions(workspacePath, decisionsKey(task.worktreePath, task.branch));
|
|
6318
6653
|
resetSubAgents(task.worktreePath);
|
|
@@ -6336,6 +6671,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6336
6671
|
if (columnOf(existing.id) !== "backlog") {
|
|
6337
6672
|
throw new Error("\u53EA\u6709 Backlog \u7684\u4EFB\u52D9\u53EF\u4EE5\u7DE8\u8F2F");
|
|
6338
6673
|
}
|
|
6674
|
+
if (p.effort !== void 0 && !AGENT_EFFORTS.includes(p.effort)) invalid("unknown effort");
|
|
6339
6675
|
let gitPatch = {};
|
|
6340
6676
|
const nextProject = p.projectPath || existing.projectPath;
|
|
6341
6677
|
const nextBranch = p.branch === void 0 ? "" : str(p.branch, "branch").trim();
|
|
@@ -6355,7 +6691,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6355
6691
|
}
|
|
6356
6692
|
const info = await getGitInfo(nextProject);
|
|
6357
6693
|
if (!info.isRepo) throw new Error("\u76EE\u6A19\u8CC7\u6599\u593E\u4E0D\u662F git repository");
|
|
6358
|
-
const projectName =
|
|
6694
|
+
const projectName = path22.basename(nextProject);
|
|
6359
6695
|
const workspacePath = projectWorkstationPath(resolveWorkstationPath(getSettings()), projectName);
|
|
6360
6696
|
await teardownTask(existing.id);
|
|
6361
6697
|
await removeTaskWorktree(existing);
|
|
@@ -6449,7 +6785,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6449
6785
|
"claude:sessionExists": (taskId) => {
|
|
6450
6786
|
const task = requireTask(taskId);
|
|
6451
6787
|
if (!task.worktreePath) return false;
|
|
6452
|
-
return
|
|
6788
|
+
return fs18.existsSync(claudeSessionFile(task.worktreePath, executorSessionId(task.id, task.runId)));
|
|
6453
6789
|
},
|
|
6454
6790
|
"sessions:list": async () => {
|
|
6455
6791
|
const keys = await sessions.list();
|
|
@@ -6496,7 +6832,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6496
6832
|
const task = requireTask(taskId);
|
|
6497
6833
|
if (!task.worktreePath || !task.workspacePath) return "task has no worktree";
|
|
6498
6834
|
const dir = agentArtifactsPath(task.workspacePath, task.worktreePath);
|
|
6499
|
-
if (!
|
|
6835
|
+
if (!fs18.existsSync(dir)) return "artifacts directory does not exist yet";
|
|
6500
6836
|
return getPlatform().openPath(dir);
|
|
6501
6837
|
},
|
|
6502
6838
|
"task:getSpecs": async (taskId) => {
|
|
@@ -6630,7 +6966,7 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6630
6966
|
}
|
|
6631
6967
|
await removeTaskWorktree(task);
|
|
6632
6968
|
if (task.workspacePath) {
|
|
6633
|
-
|
|
6969
|
+
fs18.rmSync(taskAttachmentStagingPath(task.workspacePath, task.id), { recursive: true, force: true });
|
|
6634
6970
|
}
|
|
6635
6971
|
clearConversation(task.id);
|
|
6636
6972
|
removeTask(task.id);
|
|
@@ -6683,9 +7019,9 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6683
7019
|
// Watch the containing directory, not the file: JsonStore renames a temp
|
|
6684
7020
|
// file over the target and so replaces the inode on every save.
|
|
6685
7021
|
watchStore() {
|
|
6686
|
-
const file =
|
|
7022
|
+
const file = path22.basename(getStorePath());
|
|
6687
7023
|
let debounce = null;
|
|
6688
|
-
storeWatcher =
|
|
7024
|
+
storeWatcher = fs18.watch(path22.dirname(getStorePath()), (_event, filename) => {
|
|
6689
7025
|
if (filename !== file) return;
|
|
6690
7026
|
if (debounce) clearTimeout(debounce);
|
|
6691
7027
|
debounce = setTimeout(() => {
|
|
@@ -6697,6 +7033,9 @@ function createCore({ sessions, bus, version, homeDir, ghRunner, githubAuthStatu
|
|
|
6697
7033
|
});
|
|
6698
7034
|
return () => storeWatcher?.close();
|
|
6699
7035
|
},
|
|
7036
|
+
warmModelCatalog() {
|
|
7037
|
+
models.warm();
|
|
7038
|
+
},
|
|
6700
7039
|
shutdown() {
|
|
6701
7040
|
sessions.shutdown();
|
|
6702
7041
|
cancelAllChatSends();
|
|
@@ -6718,9 +7057,9 @@ function createSessionBackend(sink, choice) {
|
|
|
6718
7057
|
}
|
|
6719
7058
|
|
|
6720
7059
|
// packages/core/src/web-server.ts
|
|
6721
|
-
import
|
|
7060
|
+
import fs19 from "fs";
|
|
6722
7061
|
import http from "http";
|
|
6723
|
-
import
|
|
7062
|
+
import path23 from "path";
|
|
6724
7063
|
import { timingSafeEqual } from "crypto";
|
|
6725
7064
|
import { WebSocketServer } from "ws";
|
|
6726
7065
|
var TOKEN_COOKIE = "vf_token";
|
|
@@ -6773,14 +7112,14 @@ function resolveStatic(root, urlPath) {
|
|
|
6773
7112
|
return null;
|
|
6774
7113
|
}
|
|
6775
7114
|
if (decoded.includes("\0")) return null;
|
|
6776
|
-
const rel =
|
|
6777
|
-
const base2 =
|
|
6778
|
-
const candidates = [rel,
|
|
7115
|
+
const rel = path23.posix.normalize(decoded).replace(/^\/+/, "");
|
|
7116
|
+
const base2 = path23.resolve(root);
|
|
7117
|
+
const candidates = [rel, path23.posix.join(rel, "index.html"), `${rel}.html`];
|
|
6779
7118
|
for (const c of candidates) {
|
|
6780
|
-
const full =
|
|
6781
|
-
if (full !== base2 && !full.startsWith(base2 +
|
|
7119
|
+
const full = path23.resolve(base2, c);
|
|
7120
|
+
if (full !== base2 && !full.startsWith(base2 + path23.sep)) return null;
|
|
6782
7121
|
try {
|
|
6783
|
-
if (
|
|
7122
|
+
if (fs19.statSync(full).isFile()) return full;
|
|
6784
7123
|
} catch {
|
|
6785
7124
|
}
|
|
6786
7125
|
}
|
|
@@ -6841,7 +7180,7 @@ async function startWebServer(options) {
|
|
|
6841
7180
|
send(res, 404, "Not found");
|
|
6842
7181
|
return;
|
|
6843
7182
|
}
|
|
6844
|
-
const ext =
|
|
7183
|
+
const ext = path23.extname(file).toLowerCase();
|
|
6845
7184
|
const immutable = url.pathname.startsWith("/_next/static/");
|
|
6846
7185
|
res.writeHead(200, {
|
|
6847
7186
|
"Content-Type": CONTENT_TYPES[ext] ?? "application/octet-stream",
|
|
@@ -6854,7 +7193,7 @@ async function startWebServer(options) {
|
|
|
6854
7193
|
res.end();
|
|
6855
7194
|
return;
|
|
6856
7195
|
}
|
|
6857
|
-
|
|
7196
|
+
fs19.createReadStream(file).pipe(res);
|
|
6858
7197
|
}
|
|
6859
7198
|
const wss = new WebSocketServer({ noServer: true, maxPayload: MAX_PAYLOAD });
|
|
6860
7199
|
function reject(socket, status, reason) {
|
|
@@ -7018,6 +7357,7 @@ async function startHost(options) {
|
|
|
7018
7357
|
lock.port = server.port;
|
|
7019
7358
|
updateLock(options.userDataDir, lock);
|
|
7020
7359
|
const stopWatch = core.watchStore();
|
|
7360
|
+
core.warmModelCatalog();
|
|
7021
7361
|
const stop = () => stopping ??= (async () => {
|
|
7022
7362
|
stopWatch();
|
|
7023
7363
|
core.shutdown();
|
|
@@ -7034,7 +7374,7 @@ function loginUrl(lock, pathname = "/home/") {
|
|
|
7034
7374
|
return `http://127.0.0.1:${lock.port}${pathname}?token=${encodeURIComponent(lock.token)}`;
|
|
7035
7375
|
}
|
|
7036
7376
|
function isWebDir(dir) {
|
|
7037
|
-
return
|
|
7377
|
+
return fs20.existsSync(path24.join(dir, "home", "index.html"));
|
|
7038
7378
|
}
|
|
7039
7379
|
|
|
7040
7380
|
// packages/cli/src/remote.ts
|
|
@@ -7151,14 +7491,14 @@ function connectToHost(lock) {
|
|
|
7151
7491
|
}
|
|
7152
7492
|
|
|
7153
7493
|
// packages/cli/src/doctor.ts
|
|
7154
|
-
import
|
|
7155
|
-
import
|
|
7156
|
-
import { execFile as
|
|
7157
|
-
import { promisify as
|
|
7494
|
+
import fs21 from "fs";
|
|
7495
|
+
import path25 from "path";
|
|
7496
|
+
import { execFile as execFile8 } from "child_process";
|
|
7497
|
+
import { promisify as promisify5 } from "util";
|
|
7158
7498
|
init_env();
|
|
7159
7499
|
init_git_bash();
|
|
7160
7500
|
init_tmux_backend();
|
|
7161
|
-
var
|
|
7501
|
+
var pexec5 = promisify5(execFile8);
|
|
7162
7502
|
function parseVersion(text) {
|
|
7163
7503
|
const m = /(\d+)\.(\d+)(?:\.(\d+))?/.exec(text);
|
|
7164
7504
|
return m ? [Number(m[1]), Number(m[2]), Number(m[3] ?? 0)] : [];
|
|
@@ -7172,7 +7512,7 @@ function versionAtLeast(have, want) {
|
|
|
7172
7512
|
}
|
|
7173
7513
|
async function run2(cmd, args) {
|
|
7174
7514
|
try {
|
|
7175
|
-
const { stdout } = await
|
|
7515
|
+
const { stdout } = await pexec5(cmd, args, { env: execEnv(), timeout: 1e4, windowsHide: true });
|
|
7176
7516
|
return String(stdout).trim();
|
|
7177
7517
|
} catch {
|
|
7178
7518
|
return null;
|
|
@@ -7292,10 +7632,10 @@ async function runDoctor(options) {
|
|
|
7292
7632
|
});
|
|
7293
7633
|
}
|
|
7294
7634
|
try {
|
|
7295
|
-
|
|
7296
|
-
const probe =
|
|
7297
|
-
|
|
7298
|
-
|
|
7635
|
+
fs21.mkdirSync(options.userDataDir, { recursive: true });
|
|
7636
|
+
const probe = path25.join(options.userDataDir, `.doctor-${process.pid}`);
|
|
7637
|
+
fs21.writeFileSync(probe, "");
|
|
7638
|
+
fs21.rmSync(probe);
|
|
7299
7639
|
checks.push({ id: "data", name: "\u8CC7\u6599\u76EE\u9304", level: "ok", detail: options.userDataDir });
|
|
7300
7640
|
} catch (err) {
|
|
7301
7641
|
checks.push({
|
|
@@ -7349,24 +7689,24 @@ Options:
|
|
|
7349
7689
|
`.trim();
|
|
7350
7690
|
function detectLayout() {
|
|
7351
7691
|
const file = fileURLToPath(import.meta.url);
|
|
7352
|
-
const here =
|
|
7353
|
-
const bundledWeb =
|
|
7354
|
-
if (isWebDir(bundledWeb) ||
|
|
7692
|
+
const here = path28.dirname(file);
|
|
7693
|
+
const bundledWeb = path28.join(here, "web");
|
|
7694
|
+
if (isWebDir(bundledWeb) || path28.basename(here) === "dist") {
|
|
7355
7695
|
return {
|
|
7356
|
-
version: readVersion(
|
|
7696
|
+
version: readVersion(path28.join(here, "..", "package.json")),
|
|
7357
7697
|
webDir: isWebDir(bundledWeb) ? bundledWeb : null,
|
|
7358
7698
|
sourceRoot: null,
|
|
7359
7699
|
cliEntry: file
|
|
7360
7700
|
};
|
|
7361
7701
|
}
|
|
7362
|
-
const root =
|
|
7702
|
+
const root = path28.resolve(here, "..", "..", "..");
|
|
7363
7703
|
const override = process.env.VIBEFLOW_WEB_DIR;
|
|
7364
|
-
const webDir = override && isWebDir(override) ? override : isWebDir(
|
|
7365
|
-
return { version: readVersion(
|
|
7704
|
+
const webDir = override && isWebDir(override) ? override : isWebDir(path28.join(root, "app")) ? path28.join(root, "app") : null;
|
|
7705
|
+
return { version: readVersion(path28.join(root, "package.json")), webDir, sourceRoot: root, cliEntry: null };
|
|
7366
7706
|
}
|
|
7367
7707
|
function readVersion(pkgPath) {
|
|
7368
7708
|
try {
|
|
7369
|
-
return JSON.parse(
|
|
7709
|
+
return JSON.parse(fs23.readFileSync(pkgPath, "utf8")).version;
|
|
7370
7710
|
} catch {
|
|
7371
7711
|
return "0.0.0";
|
|
7372
7712
|
}
|
|
@@ -7572,8 +7912,8 @@ async function cmdOpen(values, target, layout, userDataDir) {
|
|
|
7572
7912
|
console.error("\u7528\u6CD5\uFF1Avibeflow open <path>");
|
|
7573
7913
|
return 1;
|
|
7574
7914
|
}
|
|
7575
|
-
const dir =
|
|
7576
|
-
if (!
|
|
7915
|
+
const dir = path28.resolve(target);
|
|
7916
|
+
if (!fs23.existsSync(dir) || !fs23.statSync(dir).isDirectory()) {
|
|
7577
7917
|
console.error(`\u4E0D\u662F\u8CC7\u6599\u593E\uFF1A${dir}`);
|
|
7578
7918
|
return 1;
|
|
7579
7919
|
}
|