Files
ai-pull-request/app/lib/util.js
T

73 lines
2.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { spawnSync } from 'node:child_process';
/**
* 執行外部指令並回傳結果(不會因為非零結束碼而 throw)。
*
* @param {string} command 要執行的指令
* @param {string[]} args 指令參數
* @param {object} [options] spawnSync 額外設定(cwd、env、input、maxBuffer...
* @returns {{ status: number, stdout: string, stderr: string }}
*/
export function run(command, args = [], options = {}) {
const result = spawnSync(command, args, {
encoding: 'utf8',
maxBuffer: 64 * 1024 * 1024, // 64MB,避免大型 diff 被截斷
...options,
});
if (result.error) {
return { status: 1, stdout: '', stderr: String(result.error.message || result.error) };
}
return {
status: typeof result.status === 'number' ? result.status : 1,
stdout: result.stdout || '',
stderr: result.stderr || '',
};
}
/**
* 執行外部指令,若結束碼非零則 throw。
*
* @param {string} command
* @param {string[]} args
* @param {object} [options]
* @returns {string} stdout(已 trim
*/
export function runOrThrow(command, args = [], options = {}) {
const result = run(command, args, options);
if (result.status !== 0) {
const detail = (result.stderr || result.stdout || '').trim();
throw new Error(`指令失敗 (${result.status}): ${command} ${args.join(' ')}\n${detail}`);
}
return result.stdout.trim();
}
const ICONS = { info: '️', warn: '⚠️', error: '❌', success: '✅', step: '▶️' };
/** 簡單的分級日誌輸出。 */
export const log = {
info: (msg) => console.log(`${ICONS.info} ${msg}`),
warn: (msg) => console.log(`${ICONS.warn} ${msg}`),
error: (msg) => console.error(`${ICONS.error} ${msg}`),
success: (msg) => console.log(`${ICONS.success} ${msg}`),
step: (msg) => console.log(`\n${ICONS.step} ${msg}`),
};
/**
* 將敏感字串(如 token)從文字中遮蔽,避免寫入日誌。
*
* @param {string} text
* @param {string[]} secrets
* @returns {string}
*/
export function maskSecrets(text, secrets = []) {
let masked = String(text ?? '');
for (const secret of secrets) {
if (secret && secret.length >= 4) {
masked = masked.split(secret).join('***');
}
}
return masked;
}