@kw-yeh/vibeflow 4.0.5 → 4.1.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/README.md +5 -0
- package/dist/builtin-skills/pr/SKILL.md +80 -0
- package/dist/builtin-skills/visual-parity/SKILL.md +119 -0
- package/dist/builtin-skills/visual-parity/scripts/parity.py +449 -0
- package/dist/vibeflow.mjs +901 -691
- package/dist/web/404/index.html +1 -1
- package/dist/web/404.html +1 -1
- package/dist/web/_next/static/{jWK_7LAWT3o0Q0pMgqN9g → E9h3ANqgTHA4it6NLyDv5}/_buildManifest.js +1 -1
- package/dist/web/_next/static/chunks/{5896-9ec24bfe388d6661.js → 8833-76c31db372a29bb8.js} +2 -2
- package/dist/web/_next/static/chunks/pages/{_app-fde992b12936b886.js → _app-66261c34aef6a4cb.js} +1 -1
- package/dist/web/_next/static/chunks/pages/home-e16a7b49ece28424.js +11 -0
- package/dist/web/_next/static/css/{5576c1267923e491.css → c380333c2873a2b8.css} +1 -1
- package/dist/web/home/index.html +1 -1
- package/package.json +1 -1
- package/dist/web/_next/static/chunks/pages/home-24e1e93575324064.js +0 -11
- /package/dist/web/_next/static/{jWK_7LAWT3o0Q0pMgqN9g → E9h3ANqgTHA4it6NLyDv5}/_ssgManifest.js +0 -0
package/dist/vibeflow.mjs
CHANGED
|
@@ -598,9 +598,9 @@ var init_attachments = __esm({
|
|
|
598
598
|
|
|
599
599
|
// packages/tui/src/task-form.ts
|
|
600
600
|
import { spawnSync } from "child_process";
|
|
601
|
-
import
|
|
602
|
-
import
|
|
603
|
-
import
|
|
601
|
+
import fs19 from "fs";
|
|
602
|
+
import os6 from "os";
|
|
603
|
+
import path23 from "path";
|
|
604
604
|
import React, { useEffect, useRef, useState } from "react";
|
|
605
605
|
import { Box, Text, render, useApp, useInput } from "ink";
|
|
606
606
|
function newFormState(defaultAutoMode, agents) {
|
|
@@ -667,7 +667,7 @@ function visibleFields(s, ctx) {
|
|
|
667
667
|
return fields;
|
|
668
668
|
}
|
|
669
669
|
function modelOptions(ctx, agentCli, current) {
|
|
670
|
-
const listed = ctx.
|
|
670
|
+
const listed = ctx.models[agentCli] ?? [];
|
|
671
671
|
const options = ["", ...listed];
|
|
672
672
|
if (current && !listed.includes(current)) options.push(current);
|
|
673
673
|
return options;
|
|
@@ -719,17 +719,17 @@ function editorCommand(env, platform) {
|
|
|
719
719
|
return env.VISUAL?.trim() || env.EDITOR?.trim() || (platform === "win32" ? "notepad" : "vi");
|
|
720
720
|
}
|
|
721
721
|
function editInEditor(text) {
|
|
722
|
-
const dir =
|
|
723
|
-
const file =
|
|
722
|
+
const dir = fs19.mkdtempSync(path23.join(os6.tmpdir(), "vibeflow-"));
|
|
723
|
+
const file = path23.join(dir, "description.md");
|
|
724
724
|
try {
|
|
725
|
-
|
|
725
|
+
fs19.writeFileSync(file, text);
|
|
726
726
|
const cmd = editorCommand(process.env, process.platform);
|
|
727
727
|
const r = spawnSync(`${cmd} "${file}"`, { stdio: "inherit", shell: true });
|
|
728
728
|
if (r.error) return { error: `\u7121\u6CD5\u555F\u52D5\u7DE8\u8F2F\u5668\uFF08${cmd}\uFF09\uFF1A${r.error.message}` };
|
|
729
729
|
if (r.status !== 0) return { error: `\u7DE8\u8F2F\u5668\uFF08${cmd}\uFF09\u7D50\u675F\u78BC ${r.status}\uFF0C\u63CF\u8FF0\u672A\u8B8A\u66F4` };
|
|
730
|
-
return { text:
|
|
730
|
+
return { text: fs19.readFileSync(file, "utf8").replace(/\r?\n$/, "") };
|
|
731
731
|
} finally {
|
|
732
|
-
|
|
732
|
+
fs19.rmSync(dir, { recursive: true, force: true });
|
|
733
733
|
}
|
|
734
734
|
}
|
|
735
735
|
function cycle(list, current, step) {
|
|
@@ -797,10 +797,10 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
797
797
|
if (editing === "title") setS((prev) => ({ ...prev, title: draft }));
|
|
798
798
|
else if (editing === "branch") setS((prev) => ({ ...prev, branch: value2 }));
|
|
799
799
|
else if (editing === "project") {
|
|
800
|
-
if (value2) setProject(
|
|
800
|
+
if (value2) setProject(path23.resolve(value2.replace(/^~(?=$|\/)/, os6.homedir())));
|
|
801
801
|
} else if (editing === "attachments" && value2) {
|
|
802
|
-
const file =
|
|
803
|
-
if (!
|
|
802
|
+
const file = path23.resolve(value2.replace(/^~(?=$|\/)/, os6.homedir()));
|
|
803
|
+
if (!fs19.existsSync(file) || !fs19.statSync(file).isFile()) {
|
|
804
804
|
setMessage(`\u627E\u4E0D\u5230\u6A94\u6848\uFF1A${file}`);
|
|
805
805
|
return;
|
|
806
806
|
}
|
|
@@ -870,7 +870,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
870
870
|
case "title":
|
|
871
871
|
return s.title || "\uFF08\u4F8B\u5982\uFF1A\u5BE6\u4F5C\u767B\u5165\u9801\u9762\uFF09";
|
|
872
872
|
case "project": {
|
|
873
|
-
if (projectLocked) return `${s.projectPath ?
|
|
873
|
+
if (projectLocked) return `${s.projectPath ? path23.basename(s.projectPath) : "\u2014"}\uFF08\u4EFB\u52D9\u5DF2\u958B\u59CB\uFF0C\u9396\u5B9A\uFF09`;
|
|
874
874
|
if (!s.projectPath) return ctx.recent.length ? "\u2190/\u2192 \u9078\u64C7\u4F7F\u7528\u904E\u7684\u5C08\u6848\uFF0C\u6216 Enter \u8F38\u5165\u8DEF\u5F91" : "Enter \u8F38\u5165\u5C08\u6848\u8CC7\u6599\u593E\u7684\u7D55\u5C0D\u8DEF\u5F91";
|
|
875
875
|
return `${s.projectPath}${missing ? "\uFF08\u8DEF\u5F91\u907A\u5931\uFF09" : ""}`;
|
|
876
876
|
}
|
|
@@ -881,7 +881,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
881
881
|
case "branch":
|
|
882
882
|
return s.branch || "\uFF08\u7559\u7A7A\u5247\u81EA\u52D5\u547D\u540D\uFF0C\u4F8B\u5982 feature/login-fix\uFF09";
|
|
883
883
|
case "attachments":
|
|
884
|
-
return s.attachments.length ? s.attachments.map((a) =>
|
|
884
|
+
return s.attachments.length ? s.attachments.map((a) => path23.basename(a)).join("\u3001") : "\uFF08Enter \u8F38\u5165\u6A94\u6848\u8DEF\u5F91\u52A0\u5165\uFF09";
|
|
885
885
|
case "description": {
|
|
886
886
|
if (!s.description) return "\uFF08Enter \u7528\u7DE8\u8F2F\u5668\u64B0\u5BEB\uFF09";
|
|
887
887
|
const lines = s.description.split("\n");
|
|
@@ -899,7 +899,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
899
899
|
return `\u2039 ${ctx.agents.find((a) => a.id === s.agentCli)?.name ?? s.agentCli} \u203A`;
|
|
900
900
|
}
|
|
901
901
|
case "model":
|
|
902
|
-
return modelOptions(ctx, s.agentCli, s.model).length > 1 ? `\u2039 ${s.model || "\u4F7F\u7528\u9810\u8A2D model"} \u203A` : "\
|
|
902
|
+
return modelOptions(ctx, s.agentCli, s.model).length > 1 ? `\u2039 ${s.model || "\u4F7F\u7528\u9810\u8A2D model"} \u203A` : "\u7121\u6CD5\u53D6\u5F97 model list\uFF0C\u5C07\u4F7F\u7528\u9810\u8A2D model";
|
|
903
903
|
case "submit":
|
|
904
904
|
return busy ?? (s.mode === "new" ? "\uFF3B \u5EFA\u7ACB\u4EFB\u52D9 \uFF3D" : "\uFF3B \u5132\u5B58\u8B8A\u66F4 \uFF3D");
|
|
905
905
|
}
|
|
@@ -912,7 +912,7 @@ function FormView({ api, ctx, initial, initialFocus, initialMessage, pristine, o
|
|
|
912
912
|
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";
|
|
913
913
|
if (s.mode === "edit" && projectChanged(s)) return "\u66F4\u63DB\u5C08\u6848\u6703\u5728\u65B0\u5C08\u6848\u91CD\u5EFA worktree\uFF08\u6B64\u4EFB\u52D9\u5C1A\u672A\u958B\u59CB\uFF0C\u7121\u8B8A\u66F4\u6703\u907A\u5931\uFF09\u3002";
|
|
914
914
|
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`;
|
|
915
|
-
if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${
|
|
915
|
+
if (info?.isRepo && s.projectPath) return `worktree \u6703\u5EFA\u5728 ${ctx.workstationPath || "~/Desktop"}/${path23.basename(s.projectPath)}`;
|
|
916
916
|
}
|
|
917
917
|
if (field === "branch") return info?.hasRemote ? "\u586B\u5BEB\u5F8C\u6703\u5EFA\u7ACB\u540C\u540D\u5206\u652F\uFF1B\u82E5 remote \u5DF2\u6709\u9019\u500B\u5206\u652F\uFF0C\u5247\u76F4\u63A5\u53D6\u56DE\u4E26\u63A5\u7E8C\u4E0A\u9762\u7684\u5DE5\u4F5C\u3002" : "\u586B\u5BEB\u5F8C\u6703\u5EFA\u7ACB\u540C\u540D\u5206\u652F\u3002";
|
|
918
918
|
if (field === "effort") return `${EFFORTS.find((x) => x.value === s.effort).description} Claude Code \u8207 Codex \u6703\u5728\u555F\u52D5\u6B64\u4EFB\u52D9\u6642\u5957\u7528\uFF1B\u5BE6\u969B\u53EF\u7528\u7D1A\u8DDD\u4F9D model \u800C\u5B9A\u3002`;
|
|
@@ -967,11 +967,17 @@ async function loadFormContext(api) {
|
|
|
967
967
|
api.listRecentProjects(),
|
|
968
968
|
Promise.race([api.detectAgents(), timeout]).catch(() => null)
|
|
969
969
|
]).finally(() => clearTimeout(timer));
|
|
970
|
+
const lists = await Promise.all(
|
|
971
|
+
(agents ?? []).map(async (a) => {
|
|
972
|
+
const list = await api.listAgentModels(a.id).catch(() => null);
|
|
973
|
+
return [a.id, list ? list.models.map((m) => m.id) : []];
|
|
974
|
+
})
|
|
975
|
+
);
|
|
970
976
|
return {
|
|
971
977
|
ctx: {
|
|
972
978
|
recent,
|
|
973
979
|
agents,
|
|
974
|
-
|
|
980
|
+
models: Object.fromEntries(lists),
|
|
975
981
|
workstationPath: state.settings.workstationPath?.trim() ?? ""
|
|
976
982
|
},
|
|
977
983
|
autoMode: state.settings.autoMode
|
|
@@ -1023,7 +1029,7 @@ __export(src_exports, {
|
|
|
1023
1029
|
showDiff: () => showDiff
|
|
1024
1030
|
});
|
|
1025
1031
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
1026
|
-
import
|
|
1032
|
+
import path24 from "path";
|
|
1027
1033
|
import React2, { useEffect as useEffect2, useState as useState2 } from "react";
|
|
1028
1034
|
import { Box as Box2, Text as Text2, render as render2, useApp as useApp2, useInput as useInput2, useStdout } from "ink";
|
|
1029
1035
|
function findDetachKey(text) {
|
|
@@ -1285,7 +1291,7 @@ function showDiff(task) {
|
|
|
1285
1291
|
});
|
|
1286
1292
|
}
|
|
1287
1293
|
function tmuxAttach(opts, sessionName) {
|
|
1288
|
-
const conf =
|
|
1294
|
+
const conf = path24.join(opts.userDataDir, "tmux.conf");
|
|
1289
1295
|
const inOurServer = (process.env.TMUX ?? "").split(",")[0].endsWith(`/${TMUX_SOCKET}`);
|
|
1290
1296
|
const args = inOurServer ? ["-L", TMUX_SOCKET, "-f", conf, "switch-client", "-t", `=${sessionName}`] : ["-L", TMUX_SOCKET, "-f", conf, "attach-session", "-t", `=${sessionName}`];
|
|
1291
1297
|
spawnSync2("tmux", args, { stdio: "inherit", env: { ...process.env, TMUX: inOurServer ? process.env.TMUX : "" } });
|
|
@@ -1415,8 +1421,8 @@ var init_src = __esm({
|
|
|
1415
1421
|
});
|
|
1416
1422
|
|
|
1417
1423
|
// packages/cli/src/main.ts
|
|
1418
|
-
import
|
|
1419
|
-
import
|
|
1424
|
+
import fs20 from "fs";
|
|
1425
|
+
import path25 from "path";
|
|
1420
1426
|
import { fileURLToPath } from "url";
|
|
1421
1427
|
import { parseArgs as parseArgs2 } from "node:util";
|
|
1422
1428
|
|
|
@@ -1520,6 +1526,12 @@ function createNodePlatform(options = {}) {
|
|
|
1520
1526
|
userDataDir: () => userData,
|
|
1521
1527
|
sourceRoot: () => root,
|
|
1522
1528
|
cliEntry: () => options.cliEntry ?? null,
|
|
1529
|
+
builtinSkillsDir: () => {
|
|
1530
|
+
if (options.builtinSkillsDir !== void 0) return options.builtinSkillsDir;
|
|
1531
|
+
if (options.cliEntry) return path2.join(path2.dirname(options.cliEntry), "builtin-skills");
|
|
1532
|
+
if (root) return path2.join(root, "packages", "core", "builtin-skills");
|
|
1533
|
+
return null;
|
|
1534
|
+
},
|
|
1523
1535
|
openExternal: async (url) => {
|
|
1524
1536
|
const err = await openWithOs(url);
|
|
1525
1537
|
if (err) throw new Error(err);
|
|
@@ -1681,6 +1693,11 @@ function migrateStore(store) {
|
|
|
1681
1693
|
if (!store.has("recentProjects")) {
|
|
1682
1694
|
store.set("recentProjects", recentProjectsFromBoard(store.get("board")));
|
|
1683
1695
|
}
|
|
1696
|
+
const settings = store.get("settings");
|
|
1697
|
+
if (settings && "agentConnections" in settings) {
|
|
1698
|
+
const { agentConnections: _dropped, ...rest } = settings;
|
|
1699
|
+
store.set("settings", rest);
|
|
1700
|
+
}
|
|
1684
1701
|
if (persistedVersion < STATE_VERSION) {
|
|
1685
1702
|
store.set("version", STATE_VERSION);
|
|
1686
1703
|
}
|
|
@@ -1760,8 +1777,6 @@ function createBridge(t) {
|
|
|
1760
1777
|
},
|
|
1761
1778
|
setBoard: (board) => t.invoke("vibeflow:setBoard", board),
|
|
1762
1779
|
setSettings: (patch) => t.invoke("vibeflow:setSettings", patch),
|
|
1763
|
-
connectAgent: (agentId2, apiKey) => t.invoke("settings:connectAgent", { agentId: agentId2, apiKey }),
|
|
1764
|
-
refreshAgentModels: (agentId2) => t.invoke("settings:refreshAgentModels", agentId2),
|
|
1765
1780
|
getGithubAuthStatus: () => t.invoke("settings:githubAuthStatus"),
|
|
1766
1781
|
startGithubAuthLogin: () => t.invoke("settings:startGithubAuthLogin"),
|
|
1767
1782
|
cancelGithubAuthLogin: () => t.invoke("settings:cancelGithubAuthLogin"),
|
|
@@ -1776,6 +1791,8 @@ function createBridge(t) {
|
|
|
1776
1791
|
initRepository: (projectPath) => t.invoke("git:initRepository", projectPath),
|
|
1777
1792
|
/** Agent CLIs (claude / codex) actually installed on PATH. */
|
|
1778
1793
|
detectAgents: () => t.invoke("env:detectAgents"),
|
|
1794
|
+
/** Models the agent's own CLI offers; `refresh` asks the CLI instead of its cache. */
|
|
1795
|
+
listAgentModels: (agentId2, refresh = false) => t.invoke("agents:listModels", { agentId: agentId2, refresh }),
|
|
1779
1796
|
createTask: (payload) => t.invoke("vibeflow:createTask", payload),
|
|
1780
1797
|
updateTask: (payload) => t.invoke("vibeflow:updateTask", payload),
|
|
1781
1798
|
/** Clear prior execution state and prepare a fresh run of this task. */
|
|
@@ -1810,6 +1827,10 @@ function createBridge(t) {
|
|
|
1810
1827
|
setLibraryEntryDescription: (payload) => t.invoke("library:setDescription", payload),
|
|
1811
1828
|
setLibraryEntryEnabled: (payload) => t.invoke("library:setEnabled", payload),
|
|
1812
1829
|
deleteLibraryEntry: (payload) => t.invoke("library:delete", payload),
|
|
1830
|
+
/** Overwrite or bring back a shipped skill with the version this install carries. */
|
|
1831
|
+
restoreBuiltinSkill: (payload) => t.invoke("library:restoreBuiltin", payload),
|
|
1832
|
+
/** Shipped skills the user deleted, offered for restore. */
|
|
1833
|
+
listRemovedBuiltinSkills: () => t.invoke("library:removedBuiltins"),
|
|
1813
1834
|
approve: (taskId, message) => t.invoke("git:approve", { taskId, message }),
|
|
1814
1835
|
/** Use the task's agent CLI to generate a commit message for branch changes. */
|
|
1815
1836
|
generateCommitMessage: (taskId) => t.invoke("git:generateCommitMessage", taskId),
|
|
@@ -2680,7 +2701,41 @@ async function copyIgnoredEntry(projectPath, worktreePath, entry) {
|
|
|
2680
2701
|
const src = path12.join(projectPath, entry);
|
|
2681
2702
|
const dest = path12.join(worktreePath, entry);
|
|
2682
2703
|
await fs8.mkdir(path12.dirname(dest), { recursive: true });
|
|
2683
|
-
await fs8.cp(src, dest, {
|
|
2704
|
+
await fs8.cp(src, dest, {
|
|
2705
|
+
recursive: true,
|
|
2706
|
+
filter: async (from, to) => {
|
|
2707
|
+
if (!(await fs8.lstat(from)).isSymbolicLink()) return true;
|
|
2708
|
+
await copyIgnoredLink(projectPath, worktreePath, from, to);
|
|
2709
|
+
return false;
|
|
2710
|
+
}
|
|
2711
|
+
});
|
|
2712
|
+
}
|
|
2713
|
+
async function copyIgnoredLink(projectPath, worktreePath, from, to) {
|
|
2714
|
+
const raw = (await fs8.readlink(from)).replace(/^\\\\\?\\/, "");
|
|
2715
|
+
const resolved = path12.resolve(path12.dirname(from), raw);
|
|
2716
|
+
const inProject = path12.relative(projectPath, resolved);
|
|
2717
|
+
const insideProject = inProject !== "" && !inProject.startsWith("..") && !path12.isAbsolute(inProject);
|
|
2718
|
+
const target = insideProject ? path12.join(worktreePath, inProject) : resolved;
|
|
2719
|
+
let isDirectory;
|
|
2720
|
+
try {
|
|
2721
|
+
isDirectory = (await fs8.stat(resolved)).isDirectory();
|
|
2722
|
+
} catch {
|
|
2723
|
+
return;
|
|
2724
|
+
}
|
|
2725
|
+
await fs8.mkdir(path12.dirname(to), { recursive: true });
|
|
2726
|
+
if (process.platform !== "win32") {
|
|
2727
|
+
await fs8.symlink(path12.isAbsolute(raw) ? target : raw, to);
|
|
2728
|
+
return;
|
|
2729
|
+
}
|
|
2730
|
+
if (isDirectory) {
|
|
2731
|
+
await fs8.symlink(target, to, "junction");
|
|
2732
|
+
return;
|
|
2733
|
+
}
|
|
2734
|
+
try {
|
|
2735
|
+
await fs8.symlink(target, to, "file");
|
|
2736
|
+
} catch {
|
|
2737
|
+
await fs8.copyFile(resolved, to);
|
|
2738
|
+
}
|
|
2684
2739
|
}
|
|
2685
2740
|
async function copySmallIgnoredFiles(projectPath, worktreePath) {
|
|
2686
2741
|
const entries = await listIgnoredCopyEntries(projectPath);
|
|
@@ -3519,8 +3574,8 @@ async function taskCommand(argv) {
|
|
|
3519
3574
|
}
|
|
3520
3575
|
|
|
3521
3576
|
// packages/cli/src/host.ts
|
|
3522
|
-
import
|
|
3523
|
-
import
|
|
3577
|
+
import fs17 from "fs";
|
|
3578
|
+
import path21 from "path";
|
|
3524
3579
|
import { randomBytes as randomBytes2 } from "crypto";
|
|
3525
3580
|
|
|
3526
3581
|
// packages/core/src/events.ts
|
|
@@ -3546,178 +3601,725 @@ var EventBus = class {
|
|
|
3546
3601
|
}
|
|
3547
3602
|
};
|
|
3548
3603
|
|
|
3549
|
-
// packages/core/src/
|
|
3550
|
-
import
|
|
3551
|
-
import
|
|
3552
|
-
import path17 from "path";
|
|
3553
|
-
import { randomUUID as randomUUID4 } from "crypto";
|
|
3604
|
+
// packages/core/src/library-builtins.ts
|
|
3605
|
+
import fs11 from "fs";
|
|
3606
|
+
import path15 from "path";
|
|
3554
3607
|
|
|
3555
|
-
// packages/core/src/
|
|
3556
|
-
|
|
3557
|
-
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
|
|
3561
|
-
|
|
3562
|
-
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
3566
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
}) : await fetch("https://api.openai.com/v1/models", {
|
|
3579
|
-
headers: {
|
|
3580
|
-
Authorization: `Bearer ${key}`
|
|
3581
|
-
}
|
|
3582
|
-
});
|
|
3583
|
-
if (!response.ok) {
|
|
3584
|
-
throw new Error(providerAuthError(response.status));
|
|
3585
|
-
}
|
|
3586
|
-
const body = await response.json();
|
|
3587
|
-
const data = Array.isArray(body.data) ? body.data : [];
|
|
3588
|
-
return sortModels(data.map(modelIdFromUnknown).filter((id) => Boolean(id)));
|
|
3608
|
+
// packages/core/src/library.ts
|
|
3609
|
+
import { createHash } from "crypto";
|
|
3610
|
+
import fs10 from "fs";
|
|
3611
|
+
import os3 from "os";
|
|
3612
|
+
import path14 from "path";
|
|
3613
|
+
var LIBRARY_DIR = "library";
|
|
3614
|
+
var LIBRARY_KINDS = ["skill", "prompt", "script"];
|
|
3615
|
+
var KIND_DIR = {
|
|
3616
|
+
skill: "skills",
|
|
3617
|
+
prompt: "prompts",
|
|
3618
|
+
script: "scripts"
|
|
3619
|
+
};
|
|
3620
|
+
var INDEX_FILE = "index.json";
|
|
3621
|
+
var MAX_NAME_BYTES = 100;
|
|
3622
|
+
function libraryPaths(root) {
|
|
3623
|
+
return {
|
|
3624
|
+
root,
|
|
3625
|
+
skills: path14.join(root, KIND_DIR.skill),
|
|
3626
|
+
prompts: path14.join(root, KIND_DIR.prompt),
|
|
3627
|
+
scripts: path14.join(root, KIND_DIR.script),
|
|
3628
|
+
plugin: path14.join(root, "plugin"),
|
|
3629
|
+
codexHome: path14.join(root, "codex-home")
|
|
3630
|
+
};
|
|
3589
3631
|
}
|
|
3590
|
-
|
|
3591
|
-
|
|
3592
|
-
init_env();
|
|
3593
|
-
import { execFile as execFile5 } from "child_process";
|
|
3594
|
-
import { promisify as promisify3 } from "util";
|
|
3595
|
-
import * as nodePty3 from "node-pty";
|
|
3596
|
-
var pexec3 = promisify3(execFile5);
|
|
3597
|
-
var GITHUB_HOST = "github.com";
|
|
3598
|
-
var DEVICE_URL = "https://github.com/login/device";
|
|
3599
|
-
var activeLogin = null;
|
|
3600
|
-
function stripAnsi(value) {
|
|
3601
|
-
return value.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/\x07/g, "");
|
|
3632
|
+
async function libraryRoot() {
|
|
3633
|
+
return path14.join(getPlatform().userDataDir(), LIBRARY_DIR);
|
|
3602
3634
|
}
|
|
3603
|
-
function
|
|
3604
|
-
|
|
3635
|
+
function truncateUtf82(value, maxBytes) {
|
|
3636
|
+
let result = "";
|
|
3637
|
+
for (const character of value) {
|
|
3638
|
+
if (Buffer.byteLength(result + character) > maxBytes) break;
|
|
3639
|
+
result += character;
|
|
3640
|
+
}
|
|
3641
|
+
return result;
|
|
3605
3642
|
}
|
|
3606
|
-
function
|
|
3607
|
-
const
|
|
3608
|
-
const
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
|
|
3643
|
+
function safeLibraryName(name) {
|
|
3644
|
+
const basename2 = name.replace(/\\/g, "/").split("/").pop() ?? "";
|
|
3645
|
+
const cleaned = truncateUtf82(
|
|
3646
|
+
basename2.replace(/[\u0000-\u001f\u007f]/g, "").trim(),
|
|
3647
|
+
MAX_NAME_BYTES
|
|
3648
|
+
);
|
|
3649
|
+
return cleaned && cleaned !== "." && cleaned !== ".." ? cleaned : "untitled";
|
|
3612
3650
|
}
|
|
3613
|
-
function
|
|
3614
|
-
|
|
3615
|
-
const active = accounts.find((account) => account.active) ?? accounts[0];
|
|
3616
|
-
if (!active) return { installed: true, authenticated: false };
|
|
3617
|
-
return {
|
|
3618
|
-
installed: true,
|
|
3619
|
-
authenticated: Boolean(active.login) && !active.error,
|
|
3620
|
-
login: active.login,
|
|
3621
|
-
gitProtocol: active.gitProtocol,
|
|
3622
|
-
error: active.error
|
|
3623
|
-
};
|
|
3651
|
+
function entryKey(kind, name) {
|
|
3652
|
+
return `${kind}/${name}`;
|
|
3624
3653
|
}
|
|
3625
|
-
function
|
|
3626
|
-
|
|
3627
|
-
return String(err);
|
|
3654
|
+
function indexPath(root) {
|
|
3655
|
+
return path14.join(root, INDEX_FILE);
|
|
3628
3656
|
}
|
|
3629
|
-
|
|
3657
|
+
function readIndexFile(root) {
|
|
3658
|
+
let raw;
|
|
3630
3659
|
try {
|
|
3631
|
-
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
3637
|
-
|
|
3638
|
-
|
|
3639
|
-
|
|
3640
|
-
const body = JSON.parse(stdout.toString());
|
|
3641
|
-
return statusFromBody(body);
|
|
3642
|
-
} catch (err) {
|
|
3643
|
-
const maybeStdout = err.stdout;
|
|
3644
|
-
if (maybeStdout) {
|
|
3645
|
-
try {
|
|
3646
|
-
const body = JSON.parse(maybeStdout.toString());
|
|
3647
|
-
return statusFromBody(body);
|
|
3648
|
-
} catch {
|
|
3649
|
-
}
|
|
3660
|
+
raw = fs10.readFileSync(indexPath(root), "utf8");
|
|
3661
|
+
} catch {
|
|
3662
|
+
return { version: 1, entries: {} };
|
|
3663
|
+
}
|
|
3664
|
+
try {
|
|
3665
|
+
const parsed = JSON.parse(raw);
|
|
3666
|
+
if (parsed && typeof parsed === "object" && "entries" in parsed) {
|
|
3667
|
+
const entries = parsed.entries;
|
|
3668
|
+
if (entries && typeof entries === "object") return { version: 1, entries };
|
|
3650
3669
|
}
|
|
3651
|
-
|
|
3652
|
-
return {
|
|
3653
|
-
installed: !/ENOENT|not found/i.test(message),
|
|
3654
|
-
authenticated: false,
|
|
3655
|
-
error: message
|
|
3656
|
-
};
|
|
3670
|
+
} catch {
|
|
3657
3671
|
}
|
|
3672
|
+
return null;
|
|
3658
3673
|
}
|
|
3659
|
-
|
|
3660
|
-
|
|
3661
|
-
if (!status.installed) throw new Error("\u627E\u4E0D\u5230 GitHub CLI\uFF08gh\uFF09\u3002");
|
|
3662
|
-
if (!status.login) return status;
|
|
3663
|
-
await pexec3(
|
|
3664
|
-
"gh",
|
|
3665
|
-
["auth", "logout", "--hostname", GITHUB_HOST, "--user", status.login],
|
|
3666
|
-
{
|
|
3667
|
-
env: execEnv(),
|
|
3668
|
-
timeout: 1e4,
|
|
3669
|
-
windowsHide: true
|
|
3670
|
-
}
|
|
3671
|
-
);
|
|
3672
|
-
return getGitHubCliAuthStatus();
|
|
3674
|
+
function readIndex(root) {
|
|
3675
|
+
return readIndexFile(root) ?? { version: 1, entries: {} };
|
|
3673
3676
|
}
|
|
3674
|
-
function
|
|
3675
|
-
|
|
3677
|
+
function indexIsCorrupt(root) {
|
|
3678
|
+
return readIndexFile(root) === null;
|
|
3679
|
+
}
|
|
3680
|
+
function writeIndex(root, index) {
|
|
3681
|
+
fs10.mkdirSync(root, { recursive: true });
|
|
3682
|
+
const file = indexPath(root);
|
|
3683
|
+
if (indexIsCorrupt(root)) fs10.renameSync(file, `${file}.corrupt-${Date.now()}`);
|
|
3684
|
+
const tmp = `${file}.${process.pid}.tmp`;
|
|
3685
|
+
fs10.writeFileSync(tmp, `${JSON.stringify(index, null, 2)}
|
|
3686
|
+
`);
|
|
3687
|
+
fs10.renameSync(tmp, file);
|
|
3688
|
+
}
|
|
3689
|
+
function ensureLibrary(root) {
|
|
3690
|
+
const paths = libraryPaths(root);
|
|
3691
|
+
for (const kind of LIBRARY_KINDS) {
|
|
3692
|
+
fs10.mkdirSync(path14.join(root, KIND_DIR[kind]), { recursive: true });
|
|
3693
|
+
}
|
|
3694
|
+
return paths;
|
|
3695
|
+
}
|
|
3696
|
+
function readSkillDescription(skillMdPath) {
|
|
3697
|
+
let content;
|
|
3676
3698
|
try {
|
|
3677
|
-
|
|
3699
|
+
content = fs10.readFileSync(skillMdPath, "utf8");
|
|
3678
3700
|
} catch {
|
|
3701
|
+
return void 0;
|
|
3679
3702
|
}
|
|
3680
|
-
|
|
3703
|
+
const lines = content.split(/\r?\n/);
|
|
3704
|
+
if (lines[0]?.trim() !== "---") return void 0;
|
|
3705
|
+
for (let i = 1; i < lines.length; i += 1) {
|
|
3706
|
+
const line = lines[i];
|
|
3707
|
+
if (line.trim() === "---") break;
|
|
3708
|
+
const match = /^description:\s*(.+)$/.exec(line);
|
|
3709
|
+
if (match) return match[1].trim().replace(/^["']|["']$/g, "");
|
|
3710
|
+
}
|
|
3711
|
+
return void 0;
|
|
3681
3712
|
}
|
|
3682
|
-
function
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3713
|
+
function entryPath(root, kind, name) {
|
|
3714
|
+
return path14.join(root, KIND_DIR[kind], name);
|
|
3715
|
+
}
|
|
3716
|
+
var HASH_IGNORED = /* @__PURE__ */ new Set(["__pycache__", ".DS_Store"]);
|
|
3717
|
+
function hashSkillDir(dir) {
|
|
3718
|
+
const files = [];
|
|
3719
|
+
const walk = (rel) => {
|
|
3720
|
+
for (const dirent of fs10.readdirSync(path14.join(dir, rel), { withFileTypes: true })) {
|
|
3721
|
+
if (HASH_IGNORED.has(dirent.name)) continue;
|
|
3722
|
+
const child = rel ? `${rel}/${dirent.name}` : dirent.name;
|
|
3723
|
+
if (fs10.statSync(path14.join(dir, child)).isDirectory()) walk(child);
|
|
3724
|
+
else files.push(child);
|
|
3725
|
+
}
|
|
3726
|
+
};
|
|
3727
|
+
walk("");
|
|
3728
|
+
const hash = createHash("sha256");
|
|
3729
|
+
for (const rel of files.sort()) {
|
|
3730
|
+
hash.update(rel).update("\0").update(fs10.readFileSync(path14.join(dir, rel))).update("\0");
|
|
3731
|
+
}
|
|
3732
|
+
return hash.digest("hex");
|
|
3733
|
+
}
|
|
3734
|
+
function builtinStatus(full, name, recorded, builtinDir) {
|
|
3691
3735
|
try {
|
|
3692
|
-
|
|
3693
|
-
|
|
3694
|
-
|
|
3695
|
-
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
env: { ...execEnv(), BROWSER: "true", TERM: "xterm-256color" },
|
|
3699
|
-
cols: 100,
|
|
3700
|
-
rows: 24
|
|
3736
|
+
const modified = hashSkillDir(full) !== recorded;
|
|
3737
|
+
let updateAvailable = false;
|
|
3738
|
+
if (modified && builtinDir) {
|
|
3739
|
+
const shipped = path14.join(builtinDir, name);
|
|
3740
|
+
if (fs10.existsSync(path14.join(shipped, "SKILL.md"))) {
|
|
3741
|
+
updateAvailable = hashSkillDir(shipped) !== recorded;
|
|
3701
3742
|
}
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
return;
|
|
3743
|
+
}
|
|
3744
|
+
return { modified, updateAvailable };
|
|
3745
|
+
} catch {
|
|
3746
|
+
return { modified: true, updateAvailable: false };
|
|
3706
3747
|
}
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3748
|
+
}
|
|
3749
|
+
function listLibrary(root, builtinDir) {
|
|
3750
|
+
const index = readIndex(root);
|
|
3751
|
+
const entries = [];
|
|
3752
|
+
for (const kind of LIBRARY_KINDS) {
|
|
3753
|
+
const dir = path14.join(root, KIND_DIR[kind]);
|
|
3754
|
+
let dirents;
|
|
3755
|
+
try {
|
|
3756
|
+
dirents = fs10.readdirSync(dir, { withFileTypes: true });
|
|
3757
|
+
} catch {
|
|
3758
|
+
continue;
|
|
3714
3759
|
}
|
|
3715
|
-
const
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3760
|
+
for (const dirent of dirents) {
|
|
3761
|
+
if (dirent.name.startsWith(".")) continue;
|
|
3762
|
+
const wantsDirectory = kind === "skill";
|
|
3763
|
+
let stat;
|
|
3764
|
+
try {
|
|
3765
|
+
stat = fs10.statSync(path14.join(dir, dirent.name));
|
|
3766
|
+
} catch {
|
|
3767
|
+
continue;
|
|
3768
|
+
}
|
|
3769
|
+
if (stat.isDirectory() !== wantsDirectory) continue;
|
|
3770
|
+
const name = dirent.name;
|
|
3771
|
+
const full = path14.join(dir, name);
|
|
3772
|
+
if (wantsDirectory && !fs10.existsSync(path14.join(full, "SKILL.md"))) continue;
|
|
3773
|
+
const key = entryKey(kind, name);
|
|
3774
|
+
const record = index.entries[key] ?? {};
|
|
3775
|
+
entries.push({
|
|
3776
|
+
key,
|
|
3777
|
+
kind,
|
|
3778
|
+
name,
|
|
3779
|
+
path: full,
|
|
3780
|
+
description: wantsDirectory ? readSkillDescription(path14.join(full, "SKILL.md")) : record.description,
|
|
3781
|
+
sourcePath: record.sourcePath,
|
|
3782
|
+
importedAt: record.importedAt,
|
|
3783
|
+
enabled: record.enabled !== false,
|
|
3784
|
+
builtin: wantsDirectory && record.builtinHash && builtinDir !== void 0 ? builtinStatus(full, name, record.builtinHash, builtinDir) : void 0
|
|
3785
|
+
});
|
|
3719
3786
|
}
|
|
3720
|
-
|
|
3787
|
+
}
|
|
3788
|
+
return entries.sort((a, b) => a.key.localeCompare(b.key));
|
|
3789
|
+
}
|
|
3790
|
+
function annotate(root, key, patch) {
|
|
3791
|
+
const index = readIndex(root);
|
|
3792
|
+
index.entries[key] = { ...index.entries[key], ...patch };
|
|
3793
|
+
writeIndex(root, index);
|
|
3794
|
+
}
|
|
3795
|
+
function importEntry(root, kind, sourcePath) {
|
|
3796
|
+
ensureLibrary(root);
|
|
3797
|
+
const resolved = path14.resolve(sourcePath);
|
|
3798
|
+
const stat = fs10.statSync(resolved);
|
|
3799
|
+
if (kind === "skill") {
|
|
3800
|
+
if (!stat.isDirectory()) throw new Error("skill \u5FC5\u9808\u662F\u542B SKILL.md \u7684\u76EE\u9304");
|
|
3801
|
+
if (!fs10.existsSync(path14.join(resolved, "SKILL.md"))) {
|
|
3802
|
+
throw new Error("\u76EE\u9304\u5167\u627E\u4E0D\u5230 SKILL.md");
|
|
3803
|
+
}
|
|
3804
|
+
} else if (!stat.isFile()) {
|
|
3805
|
+
throw new Error(`${kind} \u5FC5\u9808\u662F\u55AE\u4E00\u6A94\u6848`);
|
|
3806
|
+
}
|
|
3807
|
+
const name = safeLibraryName(path14.basename(resolved));
|
|
3808
|
+
const target = entryPath(root, kind, name);
|
|
3809
|
+
fs10.rmSync(target, { recursive: true, force: true });
|
|
3810
|
+
fs10.cpSync(resolved, target, { recursive: kind === "skill", dereference: true });
|
|
3811
|
+
if (kind === "script") fs10.chmodSync(target, 493);
|
|
3812
|
+
const key = entryKey(kind, name);
|
|
3813
|
+
const index = readIndex(root);
|
|
3814
|
+
const { builtinHash: _hash, builtinDeleted: _deleted, ...kept } = index.entries[key] ?? {};
|
|
3815
|
+
index.entries[key] = { ...kept, sourcePath: resolved, importedAt: Date.now(), enabled: true };
|
|
3816
|
+
writeIndex(root, index);
|
|
3817
|
+
return requireEntry(root, kind, name);
|
|
3818
|
+
}
|
|
3819
|
+
function createEntry(root, kind, rawName, content, description) {
|
|
3820
|
+
ensureLibrary(root);
|
|
3821
|
+
const name = safeLibraryName(rawName);
|
|
3822
|
+
const target = entryPath(root, kind, name);
|
|
3823
|
+
if (fs10.existsSync(target)) throw new Error(`\u5DF2\u5B58\u5728\u540C\u540D\u9805\u76EE\uFF1A${name}`);
|
|
3824
|
+
if (kind === "skill") {
|
|
3825
|
+
fs10.mkdirSync(target, { recursive: true });
|
|
3826
|
+
fs10.writeFileSync(path14.join(target, "SKILL.md"), content);
|
|
3827
|
+
} else {
|
|
3828
|
+
fs10.writeFileSync(target, content);
|
|
3829
|
+
if (kind === "script") fs10.chmodSync(target, 493);
|
|
3830
|
+
}
|
|
3831
|
+
annotate(root, entryKey(kind, name), {
|
|
3832
|
+
importedAt: Date.now(),
|
|
3833
|
+
enabled: true,
|
|
3834
|
+
description
|
|
3835
|
+
});
|
|
3836
|
+
return requireEntry(root, kind, name);
|
|
3837
|
+
}
|
|
3838
|
+
function setEntryDescription(root, kind, name, description) {
|
|
3839
|
+
annotate(root, entryKey(kind, name), { description });
|
|
3840
|
+
}
|
|
3841
|
+
function updateEntry(root, kind, name, content) {
|
|
3842
|
+
const target = entryPath(root, kind, name);
|
|
3843
|
+
if (!fs10.existsSync(target)) throw new Error(`\u627E\u4E0D\u5230\u9805\u76EE\uFF1A${name}`);
|
|
3844
|
+
fs10.writeFileSync(kind === "skill" ? path14.join(target, "SKILL.md") : target, content);
|
|
3845
|
+
return requireEntry(root, kind, name);
|
|
3846
|
+
}
|
|
3847
|
+
function readEntryContent(root, kind, name) {
|
|
3848
|
+
const target = entryPath(root, kind, name);
|
|
3849
|
+
return fs10.readFileSync(kind === "skill" ? path14.join(target, "SKILL.md") : target, "utf8");
|
|
3850
|
+
}
|
|
3851
|
+
function markBuiltinDeleted(root, key) {
|
|
3852
|
+
const index = readIndex(root);
|
|
3853
|
+
index.entries[key] = { builtinDeleted: true };
|
|
3854
|
+
writeIndex(root, index);
|
|
3855
|
+
}
|
|
3856
|
+
function deleteEntry(root, kind, name) {
|
|
3857
|
+
fs10.rmSync(entryPath(root, kind, name), { recursive: true, force: true });
|
|
3858
|
+
const key = entryKey(kind, name);
|
|
3859
|
+
const record = readIndex(root).entries[key];
|
|
3860
|
+
if (record?.builtinHash || record?.builtinDeleted) {
|
|
3861
|
+
markBuiltinDeleted(root, key);
|
|
3862
|
+
return;
|
|
3863
|
+
}
|
|
3864
|
+
const index = readIndex(root);
|
|
3865
|
+
delete index.entries[key];
|
|
3866
|
+
writeIndex(root, index);
|
|
3867
|
+
}
|
|
3868
|
+
function setEntryEnabled(root, kind, name, enabled) {
|
|
3869
|
+
annotate(root, entryKey(kind, name), { enabled });
|
|
3870
|
+
}
|
|
3871
|
+
function requireEntry(root, kind, name) {
|
|
3872
|
+
const found2 = listLibrary(root).find((e) => e.key === entryKey(kind, name));
|
|
3873
|
+
if (!found2) throw new Error(`\u9805\u76EE\u5BEB\u5165\u5F8C\u8B80\u4E0D\u56DE\u4F86\uFF1A${kind}/${name}`);
|
|
3874
|
+
return found2;
|
|
3875
|
+
}
|
|
3876
|
+
function projectSkillsDir(worktreePath) {
|
|
3877
|
+
return path14.join(worktreePath, ".claude", "skills");
|
|
3878
|
+
}
|
|
3879
|
+
function skillNamesIn(dir) {
|
|
3880
|
+
try {
|
|
3881
|
+
return fs10.readdirSync(dir, { withFileTypes: true }).filter((d) => !d.name.startsWith(".")).map((d) => d.name).filter((name) => fs10.existsSync(path14.join(dir, name, "SKILL.md"))).sort();
|
|
3882
|
+
} catch {
|
|
3883
|
+
return [];
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3886
|
+
var PLUGIN_MANIFEST_DIR = ".claude-plugin";
|
|
3887
|
+
function buildPluginDir(root) {
|
|
3888
|
+
const paths = libraryPaths(root);
|
|
3889
|
+
const enabled = listLibrary(root).filter((e) => e.kind === "skill" && e.enabled);
|
|
3890
|
+
fs10.rmSync(path14.join(paths.plugin, "skills"), { recursive: true, force: true });
|
|
3891
|
+
fs10.mkdirSync(path14.join(paths.plugin, PLUGIN_MANIFEST_DIR), { recursive: true });
|
|
3892
|
+
fs10.mkdirSync(path14.join(paths.plugin, "skills"), { recursive: true });
|
|
3893
|
+
fs10.writeFileSync(
|
|
3894
|
+
path14.join(paths.plugin, PLUGIN_MANIFEST_DIR, "plugin.json"),
|
|
3895
|
+
`${JSON.stringify(
|
|
3896
|
+
{
|
|
3897
|
+
name: "vibeflow-library",
|
|
3898
|
+
version: "1.0.0",
|
|
3899
|
+
description: "Skills delivered by VibeFlow for this task."
|
|
3900
|
+
},
|
|
3901
|
+
null,
|
|
3902
|
+
2
|
|
3903
|
+
)}
|
|
3904
|
+
`
|
|
3905
|
+
);
|
|
3906
|
+
for (const entry of enabled) {
|
|
3907
|
+
fs10.cpSync(entry.path, path14.join(paths.plugin, "skills", entry.name), {
|
|
3908
|
+
recursive: true,
|
|
3909
|
+
dereference: true
|
|
3910
|
+
});
|
|
3911
|
+
}
|
|
3912
|
+
return { pluginDir: paths.plugin, skills: enabled.map((e) => e.name) };
|
|
3913
|
+
}
|
|
3914
|
+
function linkOrSkip(target, linkPath) {
|
|
3915
|
+
if (!fs10.existsSync(target)) return false;
|
|
3916
|
+
const source = path14.resolve(target);
|
|
3917
|
+
const attempts = fs10.statSync(source).isDirectory() ? [
|
|
3918
|
+
() => fs10.symlinkSync(source, linkPath, "junction"),
|
|
3919
|
+
() => fs10.cpSync(source, linkPath, { recursive: true, dereference: true })
|
|
3920
|
+
] : [
|
|
3921
|
+
() => fs10.symlinkSync(source, linkPath),
|
|
3922
|
+
() => fs10.linkSync(source, linkPath),
|
|
3923
|
+
() => fs10.copyFileSync(source, linkPath)
|
|
3924
|
+
];
|
|
3925
|
+
for (const attempt of attempts) {
|
|
3926
|
+
try {
|
|
3927
|
+
fs10.rmSync(linkPath, { recursive: true, force: true });
|
|
3928
|
+
attempt();
|
|
3929
|
+
return true;
|
|
3930
|
+
} catch {
|
|
3931
|
+
}
|
|
3932
|
+
}
|
|
3933
|
+
return false;
|
|
3934
|
+
}
|
|
3935
|
+
function buildCodexHome(root, worktreePath, userCodexHome = path14.join(os3.homedir(), ".codex")) {
|
|
3936
|
+
const paths = libraryPaths(root);
|
|
3937
|
+
const skillsDir = path14.join(paths.codexHome, "skills");
|
|
3938
|
+
fs10.rmSync(skillsDir, { recursive: true, force: true });
|
|
3939
|
+
fs10.mkdirSync(skillsDir, { recursive: true });
|
|
3940
|
+
for (const file of ["auth.json", "config.toml"]) {
|
|
3941
|
+
linkOrSkip(path14.join(userCodexHome, file), path14.join(paths.codexHome, file));
|
|
3942
|
+
}
|
|
3943
|
+
const result = {
|
|
3944
|
+
codexHome: paths.codexHome,
|
|
3945
|
+
project: [],
|
|
3946
|
+
user: [],
|
|
3947
|
+
library: [],
|
|
3948
|
+
shadowed: []
|
|
3949
|
+
};
|
|
3950
|
+
const claimed = /* @__PURE__ */ new Map();
|
|
3951
|
+
const projectDir = worktreePath ? projectSkillsDir(worktreePath) : void 0;
|
|
3952
|
+
if (projectDir) {
|
|
3953
|
+
for (const name of skillNamesIn(projectDir)) {
|
|
3954
|
+
if (linkOrSkip(path14.join(projectDir, name), path14.join(skillsDir, name))) {
|
|
3955
|
+
result.project.push(name);
|
|
3956
|
+
claimed.set(name, "project");
|
|
3957
|
+
}
|
|
3958
|
+
}
|
|
3959
|
+
}
|
|
3960
|
+
const userDir = path14.join(userCodexHome, "skills");
|
|
3961
|
+
for (const name of skillNamesIn(userDir)) {
|
|
3962
|
+
const owner = claimed.get(name);
|
|
3963
|
+
if (owner) {
|
|
3964
|
+
result.shadowed.push({ name, keptFrom: owner, dropped: "user" });
|
|
3965
|
+
continue;
|
|
3966
|
+
}
|
|
3967
|
+
if (linkOrSkip(path14.join(userDir, name), path14.join(skillsDir, name))) {
|
|
3968
|
+
result.user.push(name);
|
|
3969
|
+
claimed.set(name, "user");
|
|
3970
|
+
}
|
|
3971
|
+
}
|
|
3972
|
+
for (const entry of listLibrary(root)) {
|
|
3973
|
+
if (entry.kind !== "skill" || !entry.enabled) continue;
|
|
3974
|
+
const owner = claimed.get(entry.name);
|
|
3975
|
+
if (owner) {
|
|
3976
|
+
result.shadowed.push({ name: entry.name, keptFrom: owner, dropped: "library" });
|
|
3977
|
+
continue;
|
|
3978
|
+
}
|
|
3979
|
+
fs10.cpSync(entry.path, path14.join(skillsDir, entry.name), {
|
|
3980
|
+
recursive: true,
|
|
3981
|
+
dereference: true
|
|
3982
|
+
});
|
|
3983
|
+
result.library.push(entry.name);
|
|
3984
|
+
}
|
|
3985
|
+
return result;
|
|
3986
|
+
}
|
|
3987
|
+
function libraryLaunchInfoAt(root, worktreePath, userCodexHome) {
|
|
3988
|
+
const entries = listLibrary(root).filter((e) => e.enabled);
|
|
3989
|
+
if (entries.length === 0) return null;
|
|
3990
|
+
const { pluginDir } = buildPluginDir(root);
|
|
3991
|
+
const { codexHome } = buildCodexHome(root, worktreePath, userCodexHome);
|
|
3992
|
+
const scripts = entries.filter((e) => e.kind === "script").map((e) => ({ name: e.name, path: e.path, description: e.description }));
|
|
3993
|
+
const promptBodies = entries.filter((e) => e.kind === "prompt").map((e) => readEntryContent(root, "prompt", e.name).trim()).filter(Boolean);
|
|
3994
|
+
const lines = [...promptBodies];
|
|
3995
|
+
if (scripts.length > 0) {
|
|
3996
|
+
lines.push(
|
|
3997
|
+
"\u53EF\u7528\u7684\u5DE5\u5177\u8173\u672C\uFF08\u9700\u8981\u6642\u81EA\u884C\u57F7\u884C\uFF0C\u4E0D\u9700\u8981\u5C31\u5225\u8DD1\uFF09\uFF1A",
|
|
3998
|
+
...scripts.map((s) => `- ${s.path}${s.description ? ` \u2014 ${s.description}` : ""}`)
|
|
3999
|
+
);
|
|
4000
|
+
}
|
|
4001
|
+
return {
|
|
4002
|
+
pluginDir,
|
|
4003
|
+
codexHome,
|
|
4004
|
+
libraryDir: root,
|
|
4005
|
+
scripts,
|
|
4006
|
+
promptText: lines.join("\n\n")
|
|
4007
|
+
};
|
|
4008
|
+
}
|
|
4009
|
+
async function libraryLaunchInfo(worktreePath) {
|
|
4010
|
+
return libraryLaunchInfoAt(await libraryRoot(), worktreePath);
|
|
4011
|
+
}
|
|
4012
|
+
|
|
4013
|
+
// packages/core/src/library-builtins.ts
|
|
4014
|
+
function builtinSkillsDir() {
|
|
4015
|
+
return getPlatform().builtinSkillsDir?.() ?? null;
|
|
4016
|
+
}
|
|
4017
|
+
function installBuiltin(root, builtinDir, name) {
|
|
4018
|
+
const target = entryPath(root, "skill", name);
|
|
4019
|
+
const staging = entryPath(root, "skill", `.${name}.installing`);
|
|
4020
|
+
fs11.rmSync(staging, { recursive: true, force: true });
|
|
4021
|
+
try {
|
|
4022
|
+
fs11.cpSync(path15.join(builtinDir, name), staging, {
|
|
4023
|
+
recursive: true,
|
|
4024
|
+
dereference: true,
|
|
4025
|
+
filter: (src) => path15.basename(src) !== "__pycache__"
|
|
4026
|
+
});
|
|
4027
|
+
fs11.rmSync(target, { recursive: true, force: true });
|
|
4028
|
+
fs11.renameSync(staging, target);
|
|
4029
|
+
} finally {
|
|
4030
|
+
fs11.rmSync(staging, { recursive: true, force: true });
|
|
4031
|
+
}
|
|
4032
|
+
return hashSkillDir(target);
|
|
4033
|
+
}
|
|
4034
|
+
function syncBuiltinSkills(root, builtinDir) {
|
|
4035
|
+
const result = { installed: [], updated: [] };
|
|
4036
|
+
if (!builtinDir) return result;
|
|
4037
|
+
const names = skillNamesIn(builtinDir);
|
|
4038
|
+
if (names.length === 0) return result;
|
|
4039
|
+
if (indexIsCorrupt(root)) {
|
|
4040
|
+
console.error("Library index.json is unreadable; skipping built-in skill sync");
|
|
4041
|
+
return result;
|
|
4042
|
+
}
|
|
4043
|
+
ensureLibrary(root);
|
|
4044
|
+
for (const name of names) {
|
|
4045
|
+
try {
|
|
4046
|
+
syncOne(root, builtinDir, name, result);
|
|
4047
|
+
} catch (err) {
|
|
4048
|
+
console.error(`Failed to sync built-in skill "${name}":`, err);
|
|
4049
|
+
}
|
|
4050
|
+
}
|
|
4051
|
+
return result;
|
|
4052
|
+
}
|
|
4053
|
+
function syncOne(root, builtinDir, name, result) {
|
|
4054
|
+
const key = entryKey("skill", name);
|
|
4055
|
+
const record = readIndex(root).entries[key] ?? {};
|
|
4056
|
+
if (record.builtinDeleted) return;
|
|
4057
|
+
const target = entryPath(root, "skill", name);
|
|
4058
|
+
if (!record.builtinHash) {
|
|
4059
|
+
if (fs11.existsSync(target)) return;
|
|
4060
|
+
annotate(root, key, {
|
|
4061
|
+
builtinHash: installBuiltin(root, builtinDir, name),
|
|
4062
|
+
enabled: true,
|
|
4063
|
+
importedAt: Date.now()
|
|
4064
|
+
});
|
|
4065
|
+
result.installed.push(name);
|
|
4066
|
+
return;
|
|
4067
|
+
}
|
|
4068
|
+
if (!fs11.existsSync(path15.join(target, "SKILL.md"))) {
|
|
4069
|
+
markBuiltinDeleted(root, key);
|
|
4070
|
+
return;
|
|
4071
|
+
}
|
|
4072
|
+
if (hashSkillDir(path15.join(builtinDir, name)) === record.builtinHash) return;
|
|
4073
|
+
if (hashSkillDir(target) !== record.builtinHash) return;
|
|
4074
|
+
annotate(root, key, { builtinHash: installBuiltin(root, builtinDir, name) });
|
|
4075
|
+
result.updated.push(name);
|
|
4076
|
+
}
|
|
4077
|
+
function restoreBuiltinSkill(root, builtinDir, name) {
|
|
4078
|
+
if (!builtinDir || safeLibraryName(name) !== name || !skillNamesIn(builtinDir).includes(name)) {
|
|
4079
|
+
throw new Error(`\u4E0D\u662F\u5167\u5EFA skill\uFF1A${name}`);
|
|
4080
|
+
}
|
|
4081
|
+
const key = entryKey("skill", name);
|
|
4082
|
+
if (fs11.existsSync(entryPath(root, "skill", name)) && !readIndex(root).entries[key]?.builtinHash) {
|
|
4083
|
+
throw new Error(`\u5DF2\u6709\u540C\u540D\u7684\u81EA\u8A02\u9805\u76EE\uFF1A${name}`);
|
|
4084
|
+
}
|
|
4085
|
+
ensureLibrary(root);
|
|
4086
|
+
const builtinHash = installBuiltin(root, builtinDir, name);
|
|
4087
|
+
const index = readIndex(root);
|
|
4088
|
+
const record = { ...index.entries[key], builtinHash, enabled: true, importedAt: Date.now() };
|
|
4089
|
+
delete record.builtinDeleted;
|
|
4090
|
+
delete record.sourcePath;
|
|
4091
|
+
index.entries[key] = record;
|
|
4092
|
+
writeIndex(root, index);
|
|
4093
|
+
const entry = listLibrary(root, builtinDir).find((e) => e.key === key);
|
|
4094
|
+
if (!entry) throw new Error(`\u9084\u539F\u5931\u6557\uFF1A${name}`);
|
|
4095
|
+
return entry;
|
|
4096
|
+
}
|
|
4097
|
+
function removedBuiltinSkills(root, builtinDir) {
|
|
4098
|
+
if (!builtinDir) return [];
|
|
4099
|
+
const index = readIndex(root);
|
|
4100
|
+
return skillNamesIn(builtinDir).filter(
|
|
4101
|
+
(name) => index.entries[entryKey("skill", name)]?.builtinDeleted === true && !fs11.existsSync(entryPath(root, "skill", name))
|
|
4102
|
+
);
|
|
4103
|
+
}
|
|
4104
|
+
async function syncBuiltinsAtStartup() {
|
|
4105
|
+
try {
|
|
4106
|
+
const { installed, updated } = syncBuiltinSkills(await libraryRoot(), builtinSkillsDir());
|
|
4107
|
+
if (installed.length || updated.length) {
|
|
4108
|
+
console.log(`Built-in skills \u2014 installed: ${installed.join(", ") || "-"}; updated: ${updated.join(", ") || "-"}`);
|
|
4109
|
+
}
|
|
4110
|
+
} catch (err) {
|
|
4111
|
+
console.error("Failed to sync built-in skills:", err);
|
|
4112
|
+
}
|
|
4113
|
+
}
|
|
4114
|
+
|
|
4115
|
+
// packages/core/src/service.ts
|
|
4116
|
+
import fs15 from "fs";
|
|
4117
|
+
import os5 from "os";
|
|
4118
|
+
import path19 from "path";
|
|
4119
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
4120
|
+
|
|
4121
|
+
// packages/core/src/agent-models.ts
|
|
4122
|
+
import { execFile as execFile5 } from "child_process";
|
|
4123
|
+
import fs12 from "fs";
|
|
4124
|
+
import os4 from "os";
|
|
4125
|
+
import path16 from "path";
|
|
4126
|
+
import { promisify as promisify3 } from "util";
|
|
4127
|
+
init_env();
|
|
4128
|
+
var pexec3 = promisify3(execFile5);
|
|
4129
|
+
var CODEX_MODELS_TIMEOUT_MS = 2e4;
|
|
4130
|
+
function builtinModels(id) {
|
|
4131
|
+
return (AGENT_CLIS.find((a) => a.id === id) ?? AGENT_CLIS[0]).models;
|
|
4132
|
+
}
|
|
4133
|
+
function defaultCodexHome() {
|
|
4134
|
+
const configured = process.env.CODEX_HOME?.trim();
|
|
4135
|
+
return configured ? configured : path16.join(os4.homedir(), ".codex");
|
|
4136
|
+
}
|
|
4137
|
+
async function defaultRunCodexModels() {
|
|
4138
|
+
const { stdout } = await pexec3("codex", ["debug", "models"], {
|
|
4139
|
+
env: execEnv(),
|
|
4140
|
+
timeout: CODEX_MODELS_TIMEOUT_MS,
|
|
4141
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
4142
|
+
windowsHide: true
|
|
4143
|
+
});
|
|
4144
|
+
return stdout;
|
|
4145
|
+
}
|
|
4146
|
+
function parseCodexCatalog(raw) {
|
|
4147
|
+
const body = JSON.parse(raw);
|
|
4148
|
+
if (!Array.isArray(body.models)) throw new Error("Codex model catalog has no models array");
|
|
4149
|
+
const seen = /* @__PURE__ */ new Set();
|
|
4150
|
+
const models = [];
|
|
4151
|
+
for (const entry of body.models) {
|
|
4152
|
+
if (!entry || typeof entry !== "object") continue;
|
|
4153
|
+
const { slug, display_name: displayName, visibility } = entry;
|
|
4154
|
+
if (typeof slug !== "string" || !slug.trim() || visibility === "hide") continue;
|
|
4155
|
+
const id = slug.trim();
|
|
4156
|
+
if (seen.has(id)) continue;
|
|
4157
|
+
seen.add(id);
|
|
4158
|
+
models.push({
|
|
4159
|
+
id,
|
|
4160
|
+
label: typeof displayName === "string" && displayName.trim() ? displayName.trim() : id
|
|
4161
|
+
});
|
|
4162
|
+
}
|
|
4163
|
+
if (models.length === 0) throw new Error("Codex model catalog lists no selectable models");
|
|
4164
|
+
return models;
|
|
4165
|
+
}
|
|
4166
|
+
function errorMessage(err) {
|
|
4167
|
+
return err instanceof Error ? err.message : String(err);
|
|
4168
|
+
}
|
|
4169
|
+
async function listAgentModels(id, opts = {}) {
|
|
4170
|
+
if (id !== "codex") return { models: builtinModels(id), source: "builtin" };
|
|
4171
|
+
if (opts.refresh) {
|
|
4172
|
+
try {
|
|
4173
|
+
const stdout = await (opts.runCodexModels ?? defaultRunCodexModels)();
|
|
4174
|
+
return { models: parseCodexCatalog(stdout), source: "codex-cli" };
|
|
4175
|
+
} catch (err) {
|
|
4176
|
+
return { models: builtinModels(id), source: "builtin", error: errorMessage(err) };
|
|
4177
|
+
}
|
|
4178
|
+
}
|
|
4179
|
+
const cachePath = path16.join(opts.codexHome ?? defaultCodexHome(), "models_cache.json");
|
|
4180
|
+
try {
|
|
4181
|
+
const raw = await fs12.promises.readFile(cachePath, "utf8");
|
|
4182
|
+
return { models: parseCodexCatalog(raw), source: "codex-cache" };
|
|
4183
|
+
} catch (err) {
|
|
4184
|
+
const missing = err.code === "ENOENT";
|
|
4185
|
+
return {
|
|
4186
|
+
models: builtinModels(id),
|
|
4187
|
+
source: "builtin",
|
|
4188
|
+
...missing ? {} : { error: errorMessage(err) }
|
|
4189
|
+
};
|
|
4190
|
+
}
|
|
4191
|
+
}
|
|
4192
|
+
|
|
4193
|
+
// packages/core/src/github-auth.ts
|
|
4194
|
+
init_env();
|
|
4195
|
+
import { execFile as execFile6 } from "child_process";
|
|
4196
|
+
import { promisify as promisify4 } from "util";
|
|
4197
|
+
import * as nodePty3 from "node-pty";
|
|
4198
|
+
var pexec4 = promisify4(execFile6);
|
|
4199
|
+
var GITHUB_HOST = "github.com";
|
|
4200
|
+
var DEVICE_URL = "https://github.com/login/device";
|
|
4201
|
+
var activeLogin = null;
|
|
4202
|
+
function stripAnsi(value) {
|
|
4203
|
+
return value.replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/\x07/g, "");
|
|
4204
|
+
}
|
|
4205
|
+
function normalizeCode(code2) {
|
|
4206
|
+
return code2.replace(/\s+/g, "").toUpperCase();
|
|
4207
|
+
}
|
|
4208
|
+
function extractDeviceCode(output) {
|
|
4209
|
+
const cleaned = stripAnsi(output);
|
|
4210
|
+
const explicit = cleaned.match(/one-time code:\s*([A-Z0-9-]+)/i);
|
|
4211
|
+
if (explicit?.[1]) return normalizeCode(explicit[1]);
|
|
4212
|
+
const fallback = cleaned.match(/\b([A-Z0-9]{4}-[A-Z0-9]{4})\b/i);
|
|
4213
|
+
return fallback?.[1] ? normalizeCode(fallback[1]) : null;
|
|
4214
|
+
}
|
|
4215
|
+
function statusFromBody(body) {
|
|
4216
|
+
const accounts = body.hosts?.[GITHUB_HOST] ?? [];
|
|
4217
|
+
const active = accounts.find((account) => account.active) ?? accounts[0];
|
|
4218
|
+
if (!active) return { installed: true, authenticated: false };
|
|
4219
|
+
return {
|
|
4220
|
+
installed: true,
|
|
4221
|
+
authenticated: Boolean(active.login) && !active.error,
|
|
4222
|
+
login: active.login,
|
|
4223
|
+
gitProtocol: active.gitProtocol,
|
|
4224
|
+
error: active.error
|
|
4225
|
+
};
|
|
4226
|
+
}
|
|
4227
|
+
function errorMessage2(err) {
|
|
4228
|
+
if (err instanceof Error) return err.message;
|
|
4229
|
+
return String(err);
|
|
4230
|
+
}
|
|
4231
|
+
async function getGitHubCliAuthStatus() {
|
|
4232
|
+
try {
|
|
4233
|
+
const { stdout } = await pexec4(
|
|
4234
|
+
"gh",
|
|
4235
|
+
["auth", "status", "--hostname", GITHUB_HOST, "--json", "hosts"],
|
|
4236
|
+
{
|
|
4237
|
+
env: execEnv(),
|
|
4238
|
+
timeout: 1e4,
|
|
4239
|
+
windowsHide: true
|
|
4240
|
+
}
|
|
4241
|
+
);
|
|
4242
|
+
const body = JSON.parse(stdout.toString());
|
|
4243
|
+
return statusFromBody(body);
|
|
4244
|
+
} catch (err) {
|
|
4245
|
+
const maybeStdout = err.stdout;
|
|
4246
|
+
if (maybeStdout) {
|
|
4247
|
+
try {
|
|
4248
|
+
const body = JSON.parse(maybeStdout.toString());
|
|
4249
|
+
return statusFromBody(body);
|
|
4250
|
+
} catch {
|
|
4251
|
+
}
|
|
4252
|
+
}
|
|
4253
|
+
const message = errorMessage2(err);
|
|
4254
|
+
return {
|
|
4255
|
+
installed: !/ENOENT|not found/i.test(message),
|
|
4256
|
+
authenticated: false,
|
|
4257
|
+
error: message
|
|
4258
|
+
};
|
|
4259
|
+
}
|
|
4260
|
+
}
|
|
4261
|
+
async function logoutGitHubCli() {
|
|
4262
|
+
const status = await getGitHubCliAuthStatus();
|
|
4263
|
+
if (!status.installed) throw new Error("\u627E\u4E0D\u5230 GitHub CLI\uFF08gh\uFF09\u3002");
|
|
4264
|
+
if (!status.login) return status;
|
|
4265
|
+
await pexec4(
|
|
4266
|
+
"gh",
|
|
4267
|
+
["auth", "logout", "--hostname", GITHUB_HOST, "--user", status.login],
|
|
4268
|
+
{
|
|
4269
|
+
env: execEnv(),
|
|
4270
|
+
timeout: 1e4,
|
|
4271
|
+
windowsHide: true
|
|
4272
|
+
}
|
|
4273
|
+
);
|
|
4274
|
+
return getGitHubCliAuthStatus();
|
|
4275
|
+
}
|
|
4276
|
+
function cancelGitHubCliLogin() {
|
|
4277
|
+
if (!activeLogin) return;
|
|
4278
|
+
try {
|
|
4279
|
+
activeLogin.kill();
|
|
4280
|
+
} catch {
|
|
4281
|
+
}
|
|
4282
|
+
activeLogin = null;
|
|
4283
|
+
}
|
|
4284
|
+
function startGitHubCliLogin(onEvent) {
|
|
4285
|
+
cancelGitHubCliLogin();
|
|
4286
|
+
onEvent({ type: "starting" });
|
|
4287
|
+
let output = "";
|
|
4288
|
+
let sentBrowserChoice = false;
|
|
4289
|
+
let emittedCode = null;
|
|
4290
|
+
let continuedAfterCode = false;
|
|
4291
|
+
let completed = false;
|
|
4292
|
+
let proc;
|
|
4293
|
+
try {
|
|
4294
|
+
proc = nodePty3.spawn(
|
|
4295
|
+
"gh",
|
|
4296
|
+
["auth", "login", "--hostname", GITHUB_HOST, "--git-protocol", "https"],
|
|
4297
|
+
{
|
|
4298
|
+
name: "xterm-256color",
|
|
4299
|
+
cwd: process.cwd(),
|
|
4300
|
+
env: { ...execEnv(), BROWSER: "true", TERM: "xterm-256color" },
|
|
4301
|
+
cols: 100,
|
|
4302
|
+
rows: 24
|
|
4303
|
+
}
|
|
4304
|
+
);
|
|
4305
|
+
} catch (err) {
|
|
4306
|
+
onEvent({ type: "error", error: errorMessage2(err) });
|
|
4307
|
+
return;
|
|
4308
|
+
}
|
|
4309
|
+
activeLogin = proc;
|
|
4310
|
+
proc.onData((data) => {
|
|
4311
|
+
output += data;
|
|
4312
|
+
const cleaned = stripAnsi(output);
|
|
4313
|
+
if (!sentBrowserChoice && cleaned.includes("Login with a web browser")) {
|
|
4314
|
+
sentBrowserChoice = true;
|
|
4315
|
+
proc.write("\r");
|
|
4316
|
+
}
|
|
4317
|
+
const code2 = extractDeviceCode(output);
|
|
4318
|
+
if (code2 && code2 !== emittedCode) {
|
|
4319
|
+
emittedCode = code2;
|
|
4320
|
+
onEvent({ type: "code", code: code2, url: DEVICE_URL });
|
|
4321
|
+
}
|
|
4322
|
+
if (code2 && !continuedAfterCode) {
|
|
3721
4323
|
continuedAfterCode = true;
|
|
3722
4324
|
proc.write("\r");
|
|
3723
4325
|
}
|
|
@@ -3743,38 +4345,38 @@ function startGitHubCliLogin(onEvent) {
|
|
|
3743
4345
|
}
|
|
3744
4346
|
|
|
3745
4347
|
// packages/core/src/board-cli.ts
|
|
3746
|
-
import
|
|
3747
|
-
import
|
|
4348
|
+
import fs13 from "fs";
|
|
4349
|
+
import path17 from "path";
|
|
3748
4350
|
import { pathToFileURL } from "url";
|
|
3749
4351
|
async function boardCliLaunchInfo() {
|
|
3750
|
-
const storeDir =
|
|
4352
|
+
const storeDir = path17.dirname(getStorePath());
|
|
3751
4353
|
const entry = getPlatform().cliEntry?.();
|
|
3752
|
-
if (entry &&
|
|
4354
|
+
if (entry && fs13.existsSync(entry)) return { storeDir, cliPath: entry };
|
|
3753
4355
|
const root = getPlatform().sourceRoot();
|
|
3754
4356
|
if (!root) return { storeDir };
|
|
3755
|
-
const cliPath =
|
|
3756
|
-
const loaderPath =
|
|
3757
|
-
if (!
|
|
4357
|
+
const cliPath = path17.join(root, "scripts", "vibeflow.mjs");
|
|
4358
|
+
const loaderPath = path17.join(root, "test", "support", "register.mjs");
|
|
4359
|
+
if (!fs13.existsSync(cliPath) || !fs13.existsSync(loaderPath)) return { storeDir };
|
|
3758
4360
|
return { storeDir, cliPath, loaderPath: pathToFileURL(loaderPath).href };
|
|
3759
4361
|
}
|
|
3760
4362
|
|
|
3761
4363
|
// packages/core/src/decisions.ts
|
|
3762
|
-
import
|
|
3763
|
-
import
|
|
4364
|
+
import fs14 from "fs";
|
|
4365
|
+
import path18 from "path";
|
|
3764
4366
|
var DECISIONS_FILE_SUFFIX = ".DECISIONS.md";
|
|
3765
4367
|
var MAX_DECISION_BYTES = 256 * 1024;
|
|
3766
4368
|
function decisionsKey(worktreePath, branch) {
|
|
3767
|
-
return worktreePath ?
|
|
4369
|
+
return worktreePath ? path18.basename(worktreePath) : branch.replace(/\//g, "-");
|
|
3768
4370
|
}
|
|
3769
4371
|
function decisionsPath(workspacePath, key) {
|
|
3770
|
-
return
|
|
4372
|
+
return path18.join(workspacePath, `${key}${DECISIONS_FILE_SUFFIX}`);
|
|
3771
4373
|
}
|
|
3772
4374
|
function readDecisions(workspacePath, key) {
|
|
3773
4375
|
const target = decisionsPath(workspacePath, key);
|
|
3774
4376
|
try {
|
|
3775
|
-
const stat =
|
|
4377
|
+
const stat = fs14.statSync(target);
|
|
3776
4378
|
if (!stat.isFile()) return { path: target, markdown: null };
|
|
3777
|
-
const buf =
|
|
4379
|
+
const buf = fs14.readFileSync(target);
|
|
3778
4380
|
return {
|
|
3779
4381
|
path: target,
|
|
3780
4382
|
markdown: buf.subarray(0, MAX_DECISION_BYTES).toString("utf8"),
|
|
@@ -3787,7 +4389,7 @@ function readDecisions(workspacePath, key) {
|
|
|
3787
4389
|
}
|
|
3788
4390
|
function deleteDecisions(workspacePath, key) {
|
|
3789
4391
|
try {
|
|
3790
|
-
|
|
4392
|
+
fs14.rmSync(decisionsPath(workspacePath, key), { force: true });
|
|
3791
4393
|
} catch {
|
|
3792
4394
|
}
|
|
3793
4395
|
}
|
|
@@ -4012,414 +4614,71 @@ ${attachmentLines.join("\n")}` : text;
|
|
|
4012
4614
|
}
|
|
4013
4615
|
}
|
|
4014
4616
|
}
|
|
4015
|
-
if (event.type === "user" && event.message?.content) {
|
|
4016
|
-
for (const block of event.message.content) {
|
|
4017
|
-
if (block.type === "tool_result") {
|
|
4018
|
-
const toolName = toolNames.get(block.tool_use_id ?? "") ?? "Tool";
|
|
4019
|
-
const raw2 = block.content;
|
|
4020
|
-
const detail = typeof raw2 === "string" ? raw2 : Array.isArray(raw2) ? raw2.map((c) => c.text ?? "").join("\n") : "";
|
|
4021
|
-
pushPhase({
|
|
4022
|
-
taskId,
|
|
4023
|
-
id: `${taskId}-${seq++}`,
|
|
4024
|
-
phaseType: "tool_result",
|
|
4025
|
-
phaseSummary: `Got result from \`${toolName}\``,
|
|
4026
|
-
phaseDetail: detail,
|
|
4027
|
-
done: true
|
|
4028
|
-
});
|
|
4029
|
-
}
|
|
4030
|
-
}
|
|
4031
|
-
}
|
|
4032
|
-
if (event.type === "result" && event.result) {
|
|
4033
|
-
const remaining = event.result.slice(accumulated.length);
|
|
4034
|
-
if (remaining) push({ taskId, delta: remaining, done: false });
|
|
4035
|
-
accumulated = event.result;
|
|
4036
|
-
}
|
|
4037
|
-
} catch {
|
|
4038
|
-
}
|
|
4039
|
-
}
|
|
4040
|
-
});
|
|
4041
|
-
proc.stderr.on("data", (raw) => {
|
|
4042
|
-
const msg = raw.toString().trim();
|
|
4043
|
-
if (msg) push({ taskId, delta: "", done: false, error: msg });
|
|
4044
|
-
});
|
|
4045
|
-
proc.on("close", (code2) => {
|
|
4046
|
-
const assistantText = accumulated || "\uFF08\u7121\u56DE\u61C9\uFF09";
|
|
4047
|
-
appendMessage(taskId, {
|
|
4048
|
-
id: randomUUID3().slice(0, 8),
|
|
4049
|
-
role: "assistant",
|
|
4050
|
-
text: assistantText,
|
|
4051
|
-
ts: Date.now()
|
|
4052
|
-
});
|
|
4053
|
-
const error = code2 !== 0 ? `Process exited with code ${code2}` : void 0;
|
|
4054
|
-
push({ taskId, delta: assistantText, done: true, error });
|
|
4055
|
-
});
|
|
4056
|
-
return () => {
|
|
4057
|
-
try {
|
|
4058
|
-
proc.kill("SIGTERM");
|
|
4059
|
-
} catch {
|
|
4060
|
-
}
|
|
4061
|
-
};
|
|
4062
|
-
}
|
|
4063
|
-
var activeProcs = /* @__PURE__ */ new Map();
|
|
4064
|
-
function cancelChatSend(taskId) {
|
|
4065
|
-
activeProcs.get(taskId)?.();
|
|
4066
|
-
activeProcs.delete(taskId);
|
|
4067
|
-
}
|
|
4068
|
-
function cancelAllChatSends() {
|
|
4069
|
-
activeProcs.forEach((kill) => kill());
|
|
4070
|
-
activeProcs.clear();
|
|
4071
|
-
}
|
|
4072
|
-
function startChatSend(opts, sender) {
|
|
4073
|
-
cancelChatSend(opts.taskId);
|
|
4074
|
-
const kill = sendChatMessage(opts, sender);
|
|
4075
|
-
activeProcs.set(opts.taskId, kill);
|
|
4076
|
-
}
|
|
4077
|
-
|
|
4078
|
-
// packages/core/src/service.ts
|
|
4079
|
-
init_attachments();
|
|
4080
|
-
|
|
4081
|
-
// packages/core/src/library.ts
|
|
4082
|
-
import fs12 from "fs";
|
|
4083
|
-
import os3 from "os";
|
|
4084
|
-
import path16 from "path";
|
|
4085
|
-
var LIBRARY_DIR = "library";
|
|
4086
|
-
var LIBRARY_KINDS = ["skill", "prompt", "script"];
|
|
4087
|
-
var KIND_DIR = {
|
|
4088
|
-
skill: "skills",
|
|
4089
|
-
prompt: "prompts",
|
|
4090
|
-
script: "scripts"
|
|
4091
|
-
};
|
|
4092
|
-
var INDEX_FILE = "index.json";
|
|
4093
|
-
var MAX_NAME_BYTES = 100;
|
|
4094
|
-
function libraryPaths(root) {
|
|
4095
|
-
return {
|
|
4096
|
-
root,
|
|
4097
|
-
skills: path16.join(root, KIND_DIR.skill),
|
|
4098
|
-
prompts: path16.join(root, KIND_DIR.prompt),
|
|
4099
|
-
scripts: path16.join(root, KIND_DIR.script),
|
|
4100
|
-
plugin: path16.join(root, "plugin"),
|
|
4101
|
-
codexHome: path16.join(root, "codex-home")
|
|
4102
|
-
};
|
|
4103
|
-
}
|
|
4104
|
-
async function libraryRoot() {
|
|
4105
|
-
return path16.join(getPlatform().userDataDir(), LIBRARY_DIR);
|
|
4106
|
-
}
|
|
4107
|
-
function truncateUtf82(value, maxBytes) {
|
|
4108
|
-
let result = "";
|
|
4109
|
-
for (const character of value) {
|
|
4110
|
-
if (Buffer.byteLength(result + character) > maxBytes) break;
|
|
4111
|
-
result += character;
|
|
4112
|
-
}
|
|
4113
|
-
return result;
|
|
4114
|
-
}
|
|
4115
|
-
function safeLibraryName(name) {
|
|
4116
|
-
const basename2 = name.replace(/\\/g, "/").split("/").pop() ?? "";
|
|
4117
|
-
const cleaned = truncateUtf82(
|
|
4118
|
-
basename2.replace(/[\u0000-\u001f\u007f]/g, "").trim(),
|
|
4119
|
-
MAX_NAME_BYTES
|
|
4120
|
-
);
|
|
4121
|
-
return cleaned && cleaned !== "." && cleaned !== ".." ? cleaned : "untitled";
|
|
4122
|
-
}
|
|
4123
|
-
function entryKey(kind, name) {
|
|
4124
|
-
return `${kind}/${name}`;
|
|
4125
|
-
}
|
|
4126
|
-
function indexPath(root) {
|
|
4127
|
-
return path16.join(root, INDEX_FILE);
|
|
4128
|
-
}
|
|
4129
|
-
function readIndex(root) {
|
|
4130
|
-
try {
|
|
4131
|
-
const parsed = JSON.parse(fs12.readFileSync(indexPath(root), "utf8"));
|
|
4132
|
-
if (parsed && typeof parsed === "object" && "entries" in parsed) {
|
|
4133
|
-
const entries = parsed.entries;
|
|
4134
|
-
if (entries && typeof entries === "object") return { version: 1, entries };
|
|
4135
|
-
}
|
|
4136
|
-
} catch {
|
|
4137
|
-
}
|
|
4138
|
-
return { version: 1, entries: {} };
|
|
4139
|
-
}
|
|
4140
|
-
function writeIndex(root, index) {
|
|
4141
|
-
fs12.mkdirSync(root, { recursive: true });
|
|
4142
|
-
fs12.writeFileSync(indexPath(root), `${JSON.stringify(index, null, 2)}
|
|
4143
|
-
`);
|
|
4144
|
-
}
|
|
4145
|
-
function ensureLibrary(root) {
|
|
4146
|
-
const paths = libraryPaths(root);
|
|
4147
|
-
for (const kind of LIBRARY_KINDS) {
|
|
4148
|
-
fs12.mkdirSync(path16.join(root, KIND_DIR[kind]), { recursive: true });
|
|
4149
|
-
}
|
|
4150
|
-
return paths;
|
|
4151
|
-
}
|
|
4152
|
-
function readSkillDescription(skillMdPath) {
|
|
4153
|
-
let content;
|
|
4154
|
-
try {
|
|
4155
|
-
content = fs12.readFileSync(skillMdPath, "utf8");
|
|
4156
|
-
} catch {
|
|
4157
|
-
return void 0;
|
|
4158
|
-
}
|
|
4159
|
-
const lines = content.split(/\r?\n/);
|
|
4160
|
-
if (lines[0]?.trim() !== "---") return void 0;
|
|
4161
|
-
for (let i = 1; i < lines.length; i += 1) {
|
|
4162
|
-
const line = lines[i];
|
|
4163
|
-
if (line.trim() === "---") break;
|
|
4164
|
-
const match = /^description:\s*(.+)$/.exec(line);
|
|
4165
|
-
if (match) return match[1].trim().replace(/^["']|["']$/g, "");
|
|
4166
|
-
}
|
|
4167
|
-
return void 0;
|
|
4168
|
-
}
|
|
4169
|
-
function entryPath(root, kind, name) {
|
|
4170
|
-
return path16.join(root, KIND_DIR[kind], name);
|
|
4171
|
-
}
|
|
4172
|
-
function listLibrary(root) {
|
|
4173
|
-
const index = readIndex(root);
|
|
4174
|
-
const entries = [];
|
|
4175
|
-
for (const kind of LIBRARY_KINDS) {
|
|
4176
|
-
const dir = path16.join(root, KIND_DIR[kind]);
|
|
4177
|
-
let dirents;
|
|
4178
|
-
try {
|
|
4179
|
-
dirents = fs12.readdirSync(dir, { withFileTypes: true });
|
|
4180
|
-
} catch {
|
|
4181
|
-
continue;
|
|
4182
|
-
}
|
|
4183
|
-
for (const dirent of dirents) {
|
|
4184
|
-
if (dirent.name.startsWith(".")) continue;
|
|
4185
|
-
const wantsDirectory = kind === "skill";
|
|
4186
|
-
let stat;
|
|
4187
|
-
try {
|
|
4188
|
-
stat = fs12.statSync(path16.join(dir, dirent.name));
|
|
4617
|
+
if (event.type === "user" && event.message?.content) {
|
|
4618
|
+
for (const block of event.message.content) {
|
|
4619
|
+
if (block.type === "tool_result") {
|
|
4620
|
+
const toolName = toolNames.get(block.tool_use_id ?? "") ?? "Tool";
|
|
4621
|
+
const raw2 = block.content;
|
|
4622
|
+
const detail = typeof raw2 === "string" ? raw2 : Array.isArray(raw2) ? raw2.map((c) => c.text ?? "").join("\n") : "";
|
|
4623
|
+
pushPhase({
|
|
4624
|
+
taskId,
|
|
4625
|
+
id: `${taskId}-${seq++}`,
|
|
4626
|
+
phaseType: "tool_result",
|
|
4627
|
+
phaseSummary: `Got result from \`${toolName}\``,
|
|
4628
|
+
phaseDetail: detail,
|
|
4629
|
+
done: true
|
|
4630
|
+
});
|
|
4631
|
+
}
|
|
4632
|
+
}
|
|
4633
|
+
}
|
|
4634
|
+
if (event.type === "result" && event.result) {
|
|
4635
|
+
const remaining = event.result.slice(accumulated.length);
|
|
4636
|
+
if (remaining) push({ taskId, delta: remaining, done: false });
|
|
4637
|
+
accumulated = event.result;
|
|
4638
|
+
}
|
|
4189
4639
|
} catch {
|
|
4190
|
-
continue;
|
|
4191
4640
|
}
|
|
4192
|
-
if (stat.isDirectory() !== wantsDirectory) continue;
|
|
4193
|
-
const name = dirent.name;
|
|
4194
|
-
const full = path16.join(dir, name);
|
|
4195
|
-
if (wantsDirectory && !fs12.existsSync(path16.join(full, "SKILL.md"))) continue;
|
|
4196
|
-
const key = entryKey(kind, name);
|
|
4197
|
-
const record = index.entries[key] ?? {};
|
|
4198
|
-
entries.push({
|
|
4199
|
-
key,
|
|
4200
|
-
kind,
|
|
4201
|
-
name,
|
|
4202
|
-
path: full,
|
|
4203
|
-
description: wantsDirectory ? readSkillDescription(path16.join(full, "SKILL.md")) : record.description,
|
|
4204
|
-
sourcePath: record.sourcePath,
|
|
4205
|
-
importedAt: record.importedAt,
|
|
4206
|
-
enabled: record.enabled !== false
|
|
4207
|
-
});
|
|
4208
|
-
}
|
|
4209
|
-
}
|
|
4210
|
-
return entries.sort((a, b) => a.key.localeCompare(b.key));
|
|
4211
|
-
}
|
|
4212
|
-
function annotate(root, key, patch) {
|
|
4213
|
-
const index = readIndex(root);
|
|
4214
|
-
index.entries[key] = { ...index.entries[key], ...patch };
|
|
4215
|
-
writeIndex(root, index);
|
|
4216
|
-
}
|
|
4217
|
-
function importEntry(root, kind, sourcePath) {
|
|
4218
|
-
ensureLibrary(root);
|
|
4219
|
-
const resolved = path16.resolve(sourcePath);
|
|
4220
|
-
const stat = fs12.statSync(resolved);
|
|
4221
|
-
if (kind === "skill") {
|
|
4222
|
-
if (!stat.isDirectory()) throw new Error("skill \u5FC5\u9808\u662F\u542B SKILL.md \u7684\u76EE\u9304");
|
|
4223
|
-
if (!fs12.existsSync(path16.join(resolved, "SKILL.md"))) {
|
|
4224
|
-
throw new Error("\u76EE\u9304\u5167\u627E\u4E0D\u5230 SKILL.md");
|
|
4225
4641
|
}
|
|
4226
|
-
} else if (!stat.isFile()) {
|
|
4227
|
-
throw new Error(`${kind} \u5FC5\u9808\u662F\u55AE\u4E00\u6A94\u6848`);
|
|
4228
|
-
}
|
|
4229
|
-
const name = safeLibraryName(path16.basename(resolved));
|
|
4230
|
-
const target = entryPath(root, kind, name);
|
|
4231
|
-
fs12.rmSync(target, { recursive: true, force: true });
|
|
4232
|
-
fs12.cpSync(resolved, target, { recursive: kind === "skill", dereference: true });
|
|
4233
|
-
if (kind === "script") fs12.chmodSync(target, 493);
|
|
4234
|
-
annotate(root, entryKey(kind, name), {
|
|
4235
|
-
sourcePath: resolved,
|
|
4236
|
-
importedAt: Date.now(),
|
|
4237
|
-
enabled: true
|
|
4238
4642
|
});
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
ensureLibrary(root);
|
|
4243
|
-
const name = safeLibraryName(rawName);
|
|
4244
|
-
const target = entryPath(root, kind, name);
|
|
4245
|
-
if (fs12.existsSync(target)) throw new Error(`\u5DF2\u5B58\u5728\u540C\u540D\u9805\u76EE\uFF1A${name}`);
|
|
4246
|
-
if (kind === "skill") {
|
|
4247
|
-
fs12.mkdirSync(target, { recursive: true });
|
|
4248
|
-
fs12.writeFileSync(path16.join(target, "SKILL.md"), content);
|
|
4249
|
-
} else {
|
|
4250
|
-
fs12.writeFileSync(target, content);
|
|
4251
|
-
if (kind === "script") fs12.chmodSync(target, 493);
|
|
4252
|
-
}
|
|
4253
|
-
annotate(root, entryKey(kind, name), {
|
|
4254
|
-
importedAt: Date.now(),
|
|
4255
|
-
enabled: true,
|
|
4256
|
-
description
|
|
4643
|
+
proc.stderr.on("data", (raw) => {
|
|
4644
|
+
const msg = raw.toString().trim();
|
|
4645
|
+
if (msg) push({ taskId, delta: "", done: false, error: msg });
|
|
4257
4646
|
});
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
if (!fs12.existsSync(target)) throw new Error(`\u627E\u4E0D\u5230\u9805\u76EE\uFF1A${name}`);
|
|
4266
|
-
fs12.writeFileSync(kind === "skill" ? path16.join(target, "SKILL.md") : target, content);
|
|
4267
|
-
return requireEntry(root, kind, name);
|
|
4268
|
-
}
|
|
4269
|
-
function readEntryContent(root, kind, name) {
|
|
4270
|
-
const target = entryPath(root, kind, name);
|
|
4271
|
-
return fs12.readFileSync(kind === "skill" ? path16.join(target, "SKILL.md") : target, "utf8");
|
|
4272
|
-
}
|
|
4273
|
-
function deleteEntry(root, kind, name) {
|
|
4274
|
-
fs12.rmSync(entryPath(root, kind, name), { recursive: true, force: true });
|
|
4275
|
-
const index = readIndex(root);
|
|
4276
|
-
delete index.entries[entryKey(kind, name)];
|
|
4277
|
-
writeIndex(root, index);
|
|
4278
|
-
}
|
|
4279
|
-
function setEntryEnabled(root, kind, name, enabled) {
|
|
4280
|
-
annotate(root, entryKey(kind, name), { enabled });
|
|
4281
|
-
}
|
|
4282
|
-
function requireEntry(root, kind, name) {
|
|
4283
|
-
const found2 = listLibrary(root).find((e) => e.key === entryKey(kind, name));
|
|
4284
|
-
if (!found2) throw new Error(`\u9805\u76EE\u5BEB\u5165\u5F8C\u8B80\u4E0D\u56DE\u4F86\uFF1A${kind}/${name}`);
|
|
4285
|
-
return found2;
|
|
4286
|
-
}
|
|
4287
|
-
function projectSkillsDir(worktreePath) {
|
|
4288
|
-
return path16.join(worktreePath, ".claude", "skills");
|
|
4289
|
-
}
|
|
4290
|
-
function skillNamesIn(dir) {
|
|
4291
|
-
try {
|
|
4292
|
-
return fs12.readdirSync(dir, { withFileTypes: true }).filter((d) => !d.name.startsWith(".")).map((d) => d.name).filter((name) => fs12.existsSync(path16.join(dir, name, "SKILL.md"))).sort();
|
|
4293
|
-
} catch {
|
|
4294
|
-
return [];
|
|
4295
|
-
}
|
|
4296
|
-
}
|
|
4297
|
-
var PLUGIN_MANIFEST_DIR = ".claude-plugin";
|
|
4298
|
-
function buildPluginDir(root) {
|
|
4299
|
-
const paths = libraryPaths(root);
|
|
4300
|
-
const enabled = listLibrary(root).filter((e) => e.kind === "skill" && e.enabled);
|
|
4301
|
-
fs12.rmSync(path16.join(paths.plugin, "skills"), { recursive: true, force: true });
|
|
4302
|
-
fs12.mkdirSync(path16.join(paths.plugin, PLUGIN_MANIFEST_DIR), { recursive: true });
|
|
4303
|
-
fs12.mkdirSync(path16.join(paths.plugin, "skills"), { recursive: true });
|
|
4304
|
-
fs12.writeFileSync(
|
|
4305
|
-
path16.join(paths.plugin, PLUGIN_MANIFEST_DIR, "plugin.json"),
|
|
4306
|
-
`${JSON.stringify(
|
|
4307
|
-
{
|
|
4308
|
-
name: "vibeflow-library",
|
|
4309
|
-
version: "1.0.0",
|
|
4310
|
-
description: "Skills delivered by VibeFlow for this task."
|
|
4311
|
-
},
|
|
4312
|
-
null,
|
|
4313
|
-
2
|
|
4314
|
-
)}
|
|
4315
|
-
`
|
|
4316
|
-
);
|
|
4317
|
-
for (const entry of enabled) {
|
|
4318
|
-
fs12.cpSync(entry.path, path16.join(paths.plugin, "skills", entry.name), {
|
|
4319
|
-
recursive: true,
|
|
4320
|
-
dereference: true
|
|
4647
|
+
proc.on("close", (code2) => {
|
|
4648
|
+
const assistantText = accumulated || "\uFF08\u7121\u56DE\u61C9\uFF09";
|
|
4649
|
+
appendMessage(taskId, {
|
|
4650
|
+
id: randomUUID3().slice(0, 8),
|
|
4651
|
+
role: "assistant",
|
|
4652
|
+
text: assistantText,
|
|
4653
|
+
ts: Date.now()
|
|
4321
4654
|
});
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
}
|
|
4325
|
-
|
|
4326
|
-
if (!fs12.existsSync(target)) return false;
|
|
4327
|
-
const source = path16.resolve(target);
|
|
4328
|
-
const attempts = fs12.statSync(source).isDirectory() ? [
|
|
4329
|
-
() => fs12.symlinkSync(source, linkPath, "junction"),
|
|
4330
|
-
() => fs12.cpSync(source, linkPath, { recursive: true, dereference: true })
|
|
4331
|
-
] : [
|
|
4332
|
-
() => fs12.symlinkSync(source, linkPath),
|
|
4333
|
-
() => fs12.linkSync(source, linkPath),
|
|
4334
|
-
() => fs12.copyFileSync(source, linkPath)
|
|
4335
|
-
];
|
|
4336
|
-
for (const attempt of attempts) {
|
|
4655
|
+
const error = code2 !== 0 ? `Process exited with code ${code2}` : void 0;
|
|
4656
|
+
push({ taskId, delta: assistantText, done: true, error });
|
|
4657
|
+
});
|
|
4658
|
+
return () => {
|
|
4337
4659
|
try {
|
|
4338
|
-
|
|
4339
|
-
attempt();
|
|
4340
|
-
return true;
|
|
4660
|
+
proc.kill("SIGTERM");
|
|
4341
4661
|
} catch {
|
|
4342
4662
|
}
|
|
4343
|
-
}
|
|
4344
|
-
return false;
|
|
4345
|
-
}
|
|
4346
|
-
function buildCodexHome(root, worktreePath, userCodexHome = path16.join(os3.homedir(), ".codex")) {
|
|
4347
|
-
const paths = libraryPaths(root);
|
|
4348
|
-
const skillsDir = path16.join(paths.codexHome, "skills");
|
|
4349
|
-
fs12.rmSync(skillsDir, { recursive: true, force: true });
|
|
4350
|
-
fs12.mkdirSync(skillsDir, { recursive: true });
|
|
4351
|
-
for (const file of ["auth.json", "config.toml"]) {
|
|
4352
|
-
linkOrSkip(path16.join(userCodexHome, file), path16.join(paths.codexHome, file));
|
|
4353
|
-
}
|
|
4354
|
-
const result = {
|
|
4355
|
-
codexHome: paths.codexHome,
|
|
4356
|
-
project: [],
|
|
4357
|
-
user: [],
|
|
4358
|
-
library: [],
|
|
4359
|
-
shadowed: []
|
|
4360
4663
|
};
|
|
4361
|
-
const claimed = /* @__PURE__ */ new Map();
|
|
4362
|
-
const projectDir = worktreePath ? projectSkillsDir(worktreePath) : void 0;
|
|
4363
|
-
if (projectDir) {
|
|
4364
|
-
for (const name of skillNamesIn(projectDir)) {
|
|
4365
|
-
if (linkOrSkip(path16.join(projectDir, name), path16.join(skillsDir, name))) {
|
|
4366
|
-
result.project.push(name);
|
|
4367
|
-
claimed.set(name, "project");
|
|
4368
|
-
}
|
|
4369
|
-
}
|
|
4370
|
-
}
|
|
4371
|
-
const userDir = path16.join(userCodexHome, "skills");
|
|
4372
|
-
for (const name of skillNamesIn(userDir)) {
|
|
4373
|
-
const owner = claimed.get(name);
|
|
4374
|
-
if (owner) {
|
|
4375
|
-
result.shadowed.push({ name, keptFrom: owner, dropped: "user" });
|
|
4376
|
-
continue;
|
|
4377
|
-
}
|
|
4378
|
-
if (linkOrSkip(path16.join(userDir, name), path16.join(skillsDir, name))) {
|
|
4379
|
-
result.user.push(name);
|
|
4380
|
-
claimed.set(name, "user");
|
|
4381
|
-
}
|
|
4382
|
-
}
|
|
4383
|
-
for (const entry of listLibrary(root)) {
|
|
4384
|
-
if (entry.kind !== "skill" || !entry.enabled) continue;
|
|
4385
|
-
const owner = claimed.get(entry.name);
|
|
4386
|
-
if (owner) {
|
|
4387
|
-
result.shadowed.push({ name: entry.name, keptFrom: owner, dropped: "library" });
|
|
4388
|
-
continue;
|
|
4389
|
-
}
|
|
4390
|
-
fs12.cpSync(entry.path, path16.join(skillsDir, entry.name), {
|
|
4391
|
-
recursive: true,
|
|
4392
|
-
dereference: true
|
|
4393
|
-
});
|
|
4394
|
-
result.library.push(entry.name);
|
|
4395
|
-
}
|
|
4396
|
-
return result;
|
|
4397
4664
|
}
|
|
4398
|
-
|
|
4399
|
-
|
|
4400
|
-
|
|
4401
|
-
|
|
4402
|
-
const { codexHome } = buildCodexHome(root, worktreePath, userCodexHome);
|
|
4403
|
-
const scripts = entries.filter((e) => e.kind === "script").map((e) => ({ name: e.name, path: e.path, description: e.description }));
|
|
4404
|
-
const promptBodies = entries.filter((e) => e.kind === "prompt").map((e) => readEntryContent(root, "prompt", e.name).trim()).filter(Boolean);
|
|
4405
|
-
const lines = [...promptBodies];
|
|
4406
|
-
if (scripts.length > 0) {
|
|
4407
|
-
lines.push(
|
|
4408
|
-
"\u53EF\u7528\u7684\u5DE5\u5177\u8173\u672C\uFF08\u9700\u8981\u6642\u81EA\u884C\u57F7\u884C\uFF0C\u4E0D\u9700\u8981\u5C31\u5225\u8DD1\uFF09\uFF1A",
|
|
4409
|
-
...scripts.map((s) => `- ${s.path}${s.description ? ` \u2014 ${s.description}` : ""}`)
|
|
4410
|
-
);
|
|
4411
|
-
}
|
|
4412
|
-
return {
|
|
4413
|
-
pluginDir,
|
|
4414
|
-
codexHome,
|
|
4415
|
-
libraryDir: root,
|
|
4416
|
-
scripts,
|
|
4417
|
-
promptText: lines.join("\n\n")
|
|
4418
|
-
};
|
|
4665
|
+
var activeProcs = /* @__PURE__ */ new Map();
|
|
4666
|
+
function cancelChatSend(taskId) {
|
|
4667
|
+
activeProcs.get(taskId)?.();
|
|
4668
|
+
activeProcs.delete(taskId);
|
|
4419
4669
|
}
|
|
4420
|
-
|
|
4421
|
-
|
|
4670
|
+
function cancelAllChatSends() {
|
|
4671
|
+
activeProcs.forEach((kill) => kill());
|
|
4672
|
+
activeProcs.clear();
|
|
4422
4673
|
}
|
|
4674
|
+
function startChatSend(opts, sender) {
|
|
4675
|
+
cancelChatSend(opts.taskId);
|
|
4676
|
+
const kill = sendChatMessage(opts, sender);
|
|
4677
|
+
activeProcs.set(opts.taskId, kill);
|
|
4678
|
+
}
|
|
4679
|
+
|
|
4680
|
+
// packages/core/src/service.ts
|
|
4681
|
+
init_attachments();
|
|
4423
4682
|
|
|
4424
4683
|
// packages/core/src/launch.ts
|
|
4425
4684
|
var ARTIFACTS_DIR_SUFFIX2 = ".artifacts";
|
|
@@ -4703,13 +4962,13 @@ function createCore({ sessions, bus, version }) {
|
|
|
4703
4962
|
}
|
|
4704
4963
|
function claudeSessionFile(cwd, sessionId) {
|
|
4705
4964
|
const munged = cwd.replace(/[^a-zA-Z0-9]/g, "-");
|
|
4706
|
-
return
|
|
4965
|
+
return path19.join(os5.homedir(), ".claude", "projects", munged, `${sessionId}.jsonl`);
|
|
4707
4966
|
}
|
|
4708
4967
|
async function removeTaskWorktree(task) {
|
|
4709
4968
|
if (!task.projectPath || !task.worktreePath) return;
|
|
4710
4969
|
const branch = task.branch || fallbackBranchName(task.id);
|
|
4711
4970
|
await removeWorktree(task.projectPath, task.worktreePath);
|
|
4712
|
-
deleteArtifacts(task.workspacePath ??
|
|
4971
|
+
deleteArtifacts(task.workspacePath ?? path19.dirname(task.worktreePath), task.worktreePath);
|
|
4713
4972
|
await deleteBranch(task.projectPath, branch);
|
|
4714
4973
|
}
|
|
4715
4974
|
const handlers = {
|
|
@@ -4720,66 +4979,7 @@ function createCore({ sessions, bus, version }) {
|
|
|
4720
4979
|
return getState();
|
|
4721
4980
|
},
|
|
4722
4981
|
"vibeflow:setSettings": (patch) => {
|
|
4723
|
-
|
|
4724
|
-
const { agentConnections: _ignored, ...rest } = p;
|
|
4725
|
-
setSettings(rest);
|
|
4726
|
-
return getState();
|
|
4727
|
-
},
|
|
4728
|
-
"settings:connectAgent": async (payload) => {
|
|
4729
|
-
const { agentId: rawId, apiKey } = obj(payload, "payload");
|
|
4730
|
-
const id = agentId(rawId);
|
|
4731
|
-
const key = str(apiKey, "apiKey");
|
|
4732
|
-
const models = await fetchAgentModels(id, key);
|
|
4733
|
-
const state = getState();
|
|
4734
|
-
setSettings({
|
|
4735
|
-
agentConnections: {
|
|
4736
|
-
...state.settings.agentConnections ?? {},
|
|
4737
|
-
[id]: {
|
|
4738
|
-
connected: true,
|
|
4739
|
-
apiKey: key.trim(),
|
|
4740
|
-
models,
|
|
4741
|
-
error: void 0,
|
|
4742
|
-
updatedAt: Date.now()
|
|
4743
|
-
}
|
|
4744
|
-
}
|
|
4745
|
-
});
|
|
4746
|
-
return getState();
|
|
4747
|
-
},
|
|
4748
|
-
"settings:refreshAgentModels": async (rawId) => {
|
|
4749
|
-
const id = agentId(rawId);
|
|
4750
|
-
const connections = getState().settings.agentConnections ?? {};
|
|
4751
|
-
const existing = connections[id];
|
|
4752
|
-
if (!existing?.apiKey) {
|
|
4753
|
-
setSettings({
|
|
4754
|
-
agentConnections: {
|
|
4755
|
-
...connections,
|
|
4756
|
-
[id]: { connected: false, models: [], error: "\u5C1A\u672A\u7D81\u5B9A API key\u3002", updatedAt: Date.now() }
|
|
4757
|
-
}
|
|
4758
|
-
});
|
|
4759
|
-
return getState();
|
|
4760
|
-
}
|
|
4761
|
-
try {
|
|
4762
|
-
const models = await fetchAgentModels(id, existing.apiKey);
|
|
4763
|
-
setSettings({
|
|
4764
|
-
agentConnections: {
|
|
4765
|
-
...connections,
|
|
4766
|
-
[id]: { ...existing, connected: true, models, error: void 0, updatedAt: Date.now() }
|
|
4767
|
-
}
|
|
4768
|
-
});
|
|
4769
|
-
} catch (err) {
|
|
4770
|
-
setSettings({
|
|
4771
|
-
agentConnections: {
|
|
4772
|
-
...connections,
|
|
4773
|
-
[id]: {
|
|
4774
|
-
...existing,
|
|
4775
|
-
connected: false,
|
|
4776
|
-
models: [],
|
|
4777
|
-
error: err instanceof Error ? err.message : String(err),
|
|
4778
|
-
updatedAt: Date.now()
|
|
4779
|
-
}
|
|
4780
|
-
}
|
|
4781
|
-
});
|
|
4782
|
-
}
|
|
4982
|
+
setSettings(obj(patch, "patch"));
|
|
4783
4983
|
return getState();
|
|
4784
4984
|
},
|
|
4785
4985
|
"settings:githubAuthStatus": () => getGitHubCliAuthStatus(),
|
|
@@ -4791,6 +4991,10 @@ function createCore({ sessions, bus, version }) {
|
|
|
4791
4991
|
},
|
|
4792
4992
|
"settings:logoutGithubAuth": () => logoutGitHubCli(),
|
|
4793
4993
|
"env:detectAgents": () => detectAgents(),
|
|
4994
|
+
"agents:listModels": (payload) => {
|
|
4995
|
+
const { agentId: rawId, refresh } = obj(payload, "payload");
|
|
4996
|
+
return listAgentModels(agentId(rawId), { refresh: refresh === true });
|
|
4997
|
+
},
|
|
4794
4998
|
// A task does not exist yet when a project is picked, so these two are the
|
|
4795
4999
|
// ones that must take a path. They only inspect or `git init` it.
|
|
4796
5000
|
"git:getInfo": (projectPath) => getGitInfo(str(projectPath ?? "", "projectPath")),
|
|
@@ -4803,8 +5007,8 @@ function createCore({ sessions, bus, version }) {
|
|
|
4803
5007
|
"projects:listRecent": () => listRecentProjects(getStore()),
|
|
4804
5008
|
// `vibeflow open <path>` while a host runs: only the host writes the store.
|
|
4805
5009
|
"projects:record": (projectPath) => {
|
|
4806
|
-
const p =
|
|
4807
|
-
if (!
|
|
5010
|
+
const p = path19.resolve(str(projectPath, "projectPath"));
|
|
5011
|
+
if (!fs15.existsSync(p) || !fs15.statSync(p).isDirectory()) invalid(`not a directory: ${p}`);
|
|
4808
5012
|
recordRecentProject(getStore(), p);
|
|
4809
5013
|
return listRecentProjects(getStore());
|
|
4810
5014
|
},
|
|
@@ -4844,7 +5048,7 @@ function createCore({ sessions, bus, version }) {
|
|
|
4844
5048
|
if (!task.baseBranch) throw new Error("\u4EFB\u52D9\u6C92\u6709\u8A18\u9304 base branch\uFF0C\u7121\u6CD5\u9084\u539F worktree");
|
|
4845
5049
|
await teardownTask(task.id);
|
|
4846
5050
|
await resetWorktreeToBase(task.worktreePath, task.baseBranch);
|
|
4847
|
-
const workspacePath = task.workspacePath ??
|
|
5051
|
+
const workspacePath = task.workspacePath ?? path19.dirname(task.worktreePath);
|
|
4848
5052
|
deleteArtifacts(workspacePath, task.worktreePath);
|
|
4849
5053
|
deleteDecisions(workspacePath, decisionsKey(task.worktreePath, task.branch));
|
|
4850
5054
|
resetSubAgents(task.worktreePath);
|
|
@@ -4883,9 +5087,9 @@ function createCore({ sessions, bus, version }) {
|
|
|
4883
5087
|
if (!info.isRepo) throw new Error("\u76EE\u6A19\u8CC7\u6599\u593E\u4E0D\u662F git repository");
|
|
4884
5088
|
await teardownTask(existing.id);
|
|
4885
5089
|
await removeTaskWorktree(existing);
|
|
4886
|
-
const projectName =
|
|
5090
|
+
const projectName = path19.basename(nextProject);
|
|
4887
5091
|
const workspacePath = projectWorkstationPath(resolveWorkstationPath(getSettings()), projectName);
|
|
4888
|
-
await
|
|
5092
|
+
await fs15.promises.mkdir(workspacePath, { recursive: true });
|
|
4889
5093
|
const result = await provisionWorktree(
|
|
4890
5094
|
nextProject,
|
|
4891
5095
|
workspacePath,
|
|
@@ -4973,7 +5177,7 @@ function createCore({ sessions, bus, version }) {
|
|
|
4973
5177
|
"claude:sessionExists": (taskId) => {
|
|
4974
5178
|
const task = requireTask(taskId);
|
|
4975
5179
|
if (!task.worktreePath) return false;
|
|
4976
|
-
return
|
|
5180
|
+
return fs15.existsSync(claudeSessionFile(task.worktreePath, executorSessionId(task.id, task.runId)));
|
|
4977
5181
|
},
|
|
4978
5182
|
"sessions:list": async () => {
|
|
4979
5183
|
const keys = await sessions.list();
|
|
@@ -4992,9 +5196,9 @@ function createCore({ sessions, bus, version }) {
|
|
|
4992
5196
|
"git:getDiffEntries": (taskId, opts) => {
|
|
4993
5197
|
const task = requireTask(taskId);
|
|
4994
5198
|
if (!task.worktreePath) return [];
|
|
4995
|
-
const
|
|
5199
|
+
const fetch = opts && typeof opts === "object" ? opts.fetch : void 0;
|
|
4996
5200
|
return getWorktreeDiffEntries(task.worktreePath, task.baseBranch ?? "HEAD", {
|
|
4997
|
-
fetch: typeof
|
|
5201
|
+
fetch: typeof fetch === "boolean" ? fetch : void 0
|
|
4998
5202
|
});
|
|
4999
5203
|
},
|
|
5000
5204
|
"git:getDiffFile": (taskId, filePath) => {
|
|
@@ -5020,7 +5224,7 @@ function createCore({ sessions, bus, version }) {
|
|
|
5020
5224
|
const task = requireTask(taskId);
|
|
5021
5225
|
if (!task.worktreePath || !task.workspacePath) return "task has no worktree";
|
|
5022
5226
|
const dir = agentArtifactsPath(task.workspacePath, task.worktreePath);
|
|
5023
|
-
if (!
|
|
5227
|
+
if (!fs15.existsSync(dir)) return "artifacts directory does not exist yet";
|
|
5024
5228
|
return getPlatform().openPath(dir);
|
|
5025
5229
|
},
|
|
5026
5230
|
"task:getDecisions": (taskId) => {
|
|
@@ -5029,7 +5233,7 @@ function createCore({ sessions, bus, version }) {
|
|
|
5029
5233
|
return readDecisions(task.workspacePath, decisionsKey(task.worktreePath, task.branch));
|
|
5030
5234
|
},
|
|
5031
5235
|
"board:getCliLaunchInfo": () => boardCliLaunchInfo(),
|
|
5032
|
-
"library:list": async () => listLibrary(await libraryRoot()),
|
|
5236
|
+
"library:list": async () => listLibrary(await libraryRoot(), builtinSkillsDir()),
|
|
5033
5237
|
// The source path was picked by the user (native picker or typed path).
|
|
5034
5238
|
"library:import": async (payload) => {
|
|
5035
5239
|
const p = obj(payload, "payload");
|
|
@@ -5068,6 +5272,11 @@ function createCore({ sessions, bus, version }) {
|
|
|
5068
5272
|
deleteEntry(await libraryRoot(), libraryKind(p.kind), str(p.name, "name"));
|
|
5069
5273
|
return true;
|
|
5070
5274
|
},
|
|
5275
|
+
"library:restoreBuiltin": async (payload) => {
|
|
5276
|
+
const p = obj(payload, "payload");
|
|
5277
|
+
return restoreBuiltinSkill(await libraryRoot(), builtinSkillsDir(), str(p.name, "name"));
|
|
5278
|
+
},
|
|
5279
|
+
"library:removedBuiltins": async () => removedBuiltinSkills(await libraryRoot(), builtinSkillsDir()),
|
|
5071
5280
|
// Approve: commit everything in the worktree and push the branch upstream.
|
|
5072
5281
|
"git:approve": async (payload) => {
|
|
5073
5282
|
const p = obj(payload, "payload");
|
|
@@ -5173,9 +5382,9 @@ function createCore({ sessions, bus, version }) {
|
|
|
5173
5382
|
// Watch the containing directory, not the file: JsonStore renames a temp
|
|
5174
5383
|
// file over the target and so replaces the inode on every save.
|
|
5175
5384
|
watchStore() {
|
|
5176
|
-
const file =
|
|
5385
|
+
const file = path19.basename(getStorePath());
|
|
5177
5386
|
let debounce = null;
|
|
5178
|
-
storeWatcher =
|
|
5387
|
+
storeWatcher = fs15.watch(path19.dirname(getStorePath()), (_event, filename) => {
|
|
5179
5388
|
if (filename !== file) return;
|
|
5180
5389
|
if (debounce) clearTimeout(debounce);
|
|
5181
5390
|
debounce = setTimeout(() => {
|
|
@@ -5207,9 +5416,9 @@ function createSessionBackend(sink, choice) {
|
|
|
5207
5416
|
}
|
|
5208
5417
|
|
|
5209
5418
|
// packages/core/src/web-server.ts
|
|
5210
|
-
import
|
|
5419
|
+
import fs16 from "fs";
|
|
5211
5420
|
import http from "http";
|
|
5212
|
-
import
|
|
5421
|
+
import path20 from "path";
|
|
5213
5422
|
import { timingSafeEqual } from "crypto";
|
|
5214
5423
|
import { WebSocketServer } from "ws";
|
|
5215
5424
|
var TOKEN_COOKIE = "vf_token";
|
|
@@ -5262,14 +5471,14 @@ function resolveStatic(root, urlPath) {
|
|
|
5262
5471
|
return null;
|
|
5263
5472
|
}
|
|
5264
5473
|
if (decoded.includes("\0")) return null;
|
|
5265
|
-
const rel =
|
|
5266
|
-
const base =
|
|
5267
|
-
const candidates = [rel,
|
|
5474
|
+
const rel = path20.posix.normalize(decoded).replace(/^\/+/, "");
|
|
5475
|
+
const base = path20.resolve(root);
|
|
5476
|
+
const candidates = [rel, path20.posix.join(rel, "index.html"), `${rel}.html`];
|
|
5268
5477
|
for (const c of candidates) {
|
|
5269
|
-
const full =
|
|
5270
|
-
if (full !== base && !full.startsWith(base +
|
|
5478
|
+
const full = path20.resolve(base, c);
|
|
5479
|
+
if (full !== base && !full.startsWith(base + path20.sep)) return null;
|
|
5271
5480
|
try {
|
|
5272
|
-
if (
|
|
5481
|
+
if (fs16.statSync(full).isFile()) return full;
|
|
5273
5482
|
} catch {
|
|
5274
5483
|
}
|
|
5275
5484
|
}
|
|
@@ -5330,7 +5539,7 @@ async function startWebServer(options) {
|
|
|
5330
5539
|
send(res, 404, "Not found");
|
|
5331
5540
|
return;
|
|
5332
5541
|
}
|
|
5333
|
-
const ext =
|
|
5542
|
+
const ext = path20.extname(file).toLowerCase();
|
|
5334
5543
|
const immutable = url.pathname.startsWith("/_next/static/");
|
|
5335
5544
|
res.writeHead(200, {
|
|
5336
5545
|
"Content-Type": CONTENT_TYPES[ext] ?? "application/octet-stream",
|
|
@@ -5343,7 +5552,7 @@ async function startWebServer(options) {
|
|
|
5343
5552
|
res.end();
|
|
5344
5553
|
return;
|
|
5345
5554
|
}
|
|
5346
|
-
|
|
5555
|
+
fs16.createReadStream(file).pipe(res);
|
|
5347
5556
|
}
|
|
5348
5557
|
const wss = new WebSocketServer({ noServer: true, maxPayload: MAX_PAYLOAD });
|
|
5349
5558
|
function reject(socket, status, reason) {
|
|
@@ -5468,6 +5677,7 @@ async function startHost(options) {
|
|
|
5468
5677
|
return startHost(options);
|
|
5469
5678
|
}
|
|
5470
5679
|
try {
|
|
5680
|
+
await syncBuiltinsAtStartup();
|
|
5471
5681
|
const bus = new EventBus();
|
|
5472
5682
|
const sessions = createSessionBackend(bus.sink(), {
|
|
5473
5683
|
prefer: options.backend,
|
|
@@ -5514,7 +5724,7 @@ function loginUrl(lock, pathname = "/home/") {
|
|
|
5514
5724
|
return `http://127.0.0.1:${lock.port}${pathname}?token=${encodeURIComponent(lock.token)}`;
|
|
5515
5725
|
}
|
|
5516
5726
|
function isWebDir(dir) {
|
|
5517
|
-
return
|
|
5727
|
+
return fs17.existsSync(path21.join(dir, "home", "index.html"));
|
|
5518
5728
|
}
|
|
5519
5729
|
|
|
5520
5730
|
// packages/cli/src/remote.ts
|
|
@@ -5631,14 +5841,14 @@ function connectToHost(lock) {
|
|
|
5631
5841
|
}
|
|
5632
5842
|
|
|
5633
5843
|
// packages/cli/src/doctor.ts
|
|
5634
|
-
import
|
|
5635
|
-
import
|
|
5636
|
-
import { execFile as
|
|
5637
|
-
import { promisify as
|
|
5844
|
+
import fs18 from "fs";
|
|
5845
|
+
import path22 from "path";
|
|
5846
|
+
import { execFile as execFile7 } from "child_process";
|
|
5847
|
+
import { promisify as promisify5 } from "util";
|
|
5638
5848
|
init_env();
|
|
5639
5849
|
init_git_bash();
|
|
5640
5850
|
init_tmux_backend();
|
|
5641
|
-
var
|
|
5851
|
+
var pexec5 = promisify5(execFile7);
|
|
5642
5852
|
function parseVersion(text) {
|
|
5643
5853
|
const m = /(\d+)\.(\d+)(?:\.(\d+))?/.exec(text);
|
|
5644
5854
|
return m ? [Number(m[1]), Number(m[2]), Number(m[3] ?? 0)] : [];
|
|
@@ -5652,7 +5862,7 @@ function versionAtLeast(have, want) {
|
|
|
5652
5862
|
}
|
|
5653
5863
|
async function run(cmd, args) {
|
|
5654
5864
|
try {
|
|
5655
|
-
const { stdout } = await
|
|
5865
|
+
const { stdout } = await pexec5(cmd, args, { env: execEnv(), timeout: 1e4, windowsHide: true });
|
|
5656
5866
|
return String(stdout).trim();
|
|
5657
5867
|
} catch {
|
|
5658
5868
|
return null;
|
|
@@ -5772,10 +5982,10 @@ async function runDoctor(options) {
|
|
|
5772
5982
|
});
|
|
5773
5983
|
}
|
|
5774
5984
|
try {
|
|
5775
|
-
|
|
5776
|
-
const probe =
|
|
5777
|
-
|
|
5778
|
-
|
|
5985
|
+
fs18.mkdirSync(options.userDataDir, { recursive: true });
|
|
5986
|
+
const probe = path22.join(options.userDataDir, `.doctor-${process.pid}`);
|
|
5987
|
+
fs18.writeFileSync(probe, "");
|
|
5988
|
+
fs18.rmSync(probe);
|
|
5779
5989
|
checks.push({ id: "data", name: "\u8CC7\u6599\u76EE\u9304", level: "ok", detail: options.userDataDir });
|
|
5780
5990
|
} catch (err) {
|
|
5781
5991
|
checks.push({
|
|
@@ -5829,24 +6039,24 @@ Options:
|
|
|
5829
6039
|
`.trim();
|
|
5830
6040
|
function detectLayout() {
|
|
5831
6041
|
const file = fileURLToPath(import.meta.url);
|
|
5832
|
-
const here =
|
|
5833
|
-
const bundledWeb =
|
|
5834
|
-
if (isWebDir(bundledWeb) ||
|
|
6042
|
+
const here = path25.dirname(file);
|
|
6043
|
+
const bundledWeb = path25.join(here, "web");
|
|
6044
|
+
if (isWebDir(bundledWeb) || path25.basename(here) === "dist") {
|
|
5835
6045
|
return {
|
|
5836
|
-
version: readVersion(
|
|
6046
|
+
version: readVersion(path25.join(here, "..", "package.json")),
|
|
5837
6047
|
webDir: isWebDir(bundledWeb) ? bundledWeb : null,
|
|
5838
6048
|
sourceRoot: null,
|
|
5839
6049
|
cliEntry: file
|
|
5840
6050
|
};
|
|
5841
6051
|
}
|
|
5842
|
-
const root =
|
|
6052
|
+
const root = path25.resolve(here, "..", "..", "..");
|
|
5843
6053
|
const override = process.env.VIBEFLOW_WEB_DIR;
|
|
5844
|
-
const webDir = override && isWebDir(override) ? override : isWebDir(
|
|
5845
|
-
return { version: readVersion(
|
|
6054
|
+
const webDir = override && isWebDir(override) ? override : isWebDir(path25.join(root, "app")) ? path25.join(root, "app") : null;
|
|
6055
|
+
return { version: readVersion(path25.join(root, "package.json")), webDir, sourceRoot: root, cliEntry: null };
|
|
5846
6056
|
}
|
|
5847
6057
|
function readVersion(pkgPath) {
|
|
5848
6058
|
try {
|
|
5849
|
-
return JSON.parse(
|
|
6059
|
+
return JSON.parse(fs20.readFileSync(pkgPath, "utf8")).version;
|
|
5850
6060
|
} catch {
|
|
5851
6061
|
return "0.0.0";
|
|
5852
6062
|
}
|
|
@@ -6052,8 +6262,8 @@ async function cmdOpen(values, target, layout, userDataDir) {
|
|
|
6052
6262
|
console.error("\u7528\u6CD5\uFF1Avibeflow open <path>");
|
|
6053
6263
|
return 1;
|
|
6054
6264
|
}
|
|
6055
|
-
const dir =
|
|
6056
|
-
if (!
|
|
6265
|
+
const dir = path25.resolve(target);
|
|
6266
|
+
if (!fs20.existsSync(dir) || !fs20.statSync(dir).isDirectory()) {
|
|
6057
6267
|
console.error(`\u4E0D\u662F\u8CC7\u6599\u593E\uFF1A${dir}`);
|
|
6058
6268
|
return 1;
|
|
6059
6269
|
}
|