119 lines
3.2 KiB
TypeScript
119 lines
3.2 KiB
TypeScript
/**
|
||
* sproutclaw / mengya 命令安装扩展
|
||
*
|
||
* 启动后自动在 /usr/local/bin/ 下创建 mengya 和 sproutclaw 命令:
|
||
* - mengya:源码版(./pi-test.sh,tsx + src)
|
||
* - sproutclaw:构建版(./pi-built.sh,node dist/cli.js)
|
||
*
|
||
* 命令 /install-commands 可随时手动重装。
|
||
*/
|
||
|
||
import { existsSync, writeFileSync, chmodSync, readFileSync } from "node:fs";
|
||
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
||
|
||
const SPROUTCLAW_DIR = "/shumengya/project/agent/sproutclaw";
|
||
const BIN_DIR = "/usr/local/bin";
|
||
|
||
const COMMAND_LAUNCHERS: Record<string, string> = {
|
||
mengya: "./pi-test.sh",
|
||
};
|
||
|
||
function commandScript(launcher: string): string {
|
||
return `#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
cd ${SPROUTCLAW_DIR}
|
||
exec ${launcher} "$@"
|
||
`;
|
||
}
|
||
|
||
function sproutclawScript(): string {
|
||
return `#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
cd ${SPROUTCLAW_DIR}
|
||
|
||
case "\${1:-}" in
|
||
\tbuild)
|
||
\t\tshift
|
||
\t\texec npm run build "\$@"
|
||
\t\t;;
|
||
\trun)
|
||
\t\tshift
|
||
\t\texec ./pi-built.sh "\$@"
|
||
\t\t;;
|
||
\thelp|-h|--help)
|
||
\t\tcat <<'EOF'
|
||
sproutclaw - SproutClaw pi 构建版启动器
|
||
|
||
用法:
|
||
sproutclaw 启动 pi(构建版)
|
||
sproutclaw run [args] 同 sproutclaw
|
||
sproutclaw build 从源码构建所有包
|
||
|
||
其余参数透传给 pi,例如: sproutclaw --version
|
||
EOF
|
||
\t\t;;
|
||
\t*)
|
||
\t\texec ./pi-built.sh "\$@"
|
||
\t\t;;
|
||
esac
|
||
`;
|
||
}
|
||
|
||
/** 创建或修复命令脚本 */
|
||
function installCommand(name: string, script: string): boolean {
|
||
const path = `${BIN_DIR}/${name}`;
|
||
|
||
if (existsSync(path)) {
|
||
try {
|
||
const current = readFileSync(path, "utf-8");
|
||
if (current === script) return false;
|
||
} catch {
|
||
// Rewrite unreadable or invalid command files below.
|
||
}
|
||
}
|
||
writeFileSync(path, script, "utf-8");
|
||
chmodSync(path, 0o755);
|
||
return true;
|
||
}
|
||
|
||
export default function (pi: ExtensionAPI) {
|
||
if (process.platform === "win32") {
|
||
// On Windows, /usr/local/bin doesn't exist. Use pi-built.bat from the repo root instead.
|
||
pi.registerCommand("install-commands", {
|
||
description: "N/A on Windows: use pi-built.bat from the repo root",
|
||
handler: async (_args, ctx) => {
|
||
ctx.ui.notify("Windows 下无需安装全局命令,请直接使用项目根目录的 pi-built.bat", "info");
|
||
},
|
||
});
|
||
return;
|
||
}
|
||
|
||
for (const [name, launcher] of Object.entries(COMMAND_LAUNCHERS)) {
|
||
const created = installCommand(name, commandScript(launcher));
|
||
if (created) {
|
||
console.log(`[sproutclaw-setup] Created /usr/local/bin/${name}`);
|
||
}
|
||
}
|
||
|
||
const sproutclawCreated = installCommand("sproutclaw", sproutclawScript());
|
||
if (sproutclawCreated) {
|
||
console.log("[sproutclaw-setup] Created /usr/local/bin/sproutclaw");
|
||
}
|
||
|
||
pi.registerCommand("install-commands", {
|
||
description: "Install mengya (source) and sproutclaw (built) commands to /usr/local/bin",
|
||
handler: async (_args, ctx) => {
|
||
let count = 0;
|
||
for (const [name, launcher] of Object.entries(COMMAND_LAUNCHERS)) {
|
||
if (installCommand(name, commandScript(launcher))) count++;
|
||
}
|
||
if (installCommand("sproutclaw", sproutclawScript())) count++;
|
||
if (count > 0) {
|
||
ctx.ui.notify(`Installed ${count} command(s): mengya (source), sproutclaw (build/run)`, "success");
|
||
} else {
|
||
ctx.ui.notify("Commands already up to date", "info");
|
||
}
|
||
},
|
||
});
|
||
}
|