fix(cli): address Gemini test report — local mode, validate bug, offline flag

A. acr init --local: new local mode, no Cloudflare account required;
   config defaults to mode:local when ~/.arcrun/config.yaml missing

B. validate node-count bug: removed faulty input/output node heuristic
   that dropped start/end nodes from config check; now all nodes except
   reserved 'input' keyword must have config entries

C. acr validate --offline: skip remote component-existence and credentials
   checks; local mode also auto-skips these checks

D. parts.ts: replace require('node:fs') with static import (ES module fix)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-16 14:53:30 +08:00
parent 8e2c32e466
commit 7bd4ab0a6e
6 changed files with 68 additions and 49 deletions
+23 -4
View File
@@ -4,7 +4,7 @@
* 呼叫 arcrun.dev 取得 API Key,寫入 ~/.arcrun/config.yaml
*/
import { createInterface } from 'node:readline/promises';
import { writeFileSync, existsSync } from 'node:fs';
import { writeFileSync, existsSync, readFileSync, appendFileSync } from 'node:fs';
import { join } from 'node:path';
import chalk from 'chalk';
import { saveConfig, type ArcrunConfig } from '../lib/config.js';
@@ -16,13 +16,15 @@ async function prompt(rl: ReturnType<typeof createInterface>, question: string):
return answer.trim();
}
export async function cmdInit(options: { selfHosted?: boolean }): Promise<void> {
export async function cmdInit(options: { local?: boolean; selfHosted?: boolean }): Promise<void> {
const rl = createInterface({ input: process.stdin, output: process.stdout });
console.log(chalk.bold('\n arcrun 初始化設定\n'));
try {
if (options.selfHosted) {
if (options.local) {
await initLocal();
} else if (options.selfHosted) {
await initSelfHosted(rl);
} else {
await initStandard(rl);
@@ -32,6 +34,24 @@ export async function cmdInit(options: { selfHosted?: boolean }): Promise<void>
}
}
async function initLocal(): Promise<void> {
console.log(chalk.gray(' Local 模式:不需要 Cloudflare 帳號,直接在本機跑 workflow\n'));
const config: ArcrunConfig = {
mode: 'local',
};
saveConfig(config);
createCredentialsYamlIfMissing();
console.log(chalk.green('\n ✓ 設定完成 → ~/.arcrun/config.yamllocal 模式)\n'));
console.log(' 你可以立刻開始:');
console.log(chalk.cyan(' acr validate workflow.yaml --offline') + ' # 驗證 workflow 格式');
console.log(chalk.cyan(' acr run <workflow_name>') + ' # 本機執行\n');
console.log(chalk.gray(' Local 模式不連線 arcrun.dev,所有 workflow 在本機執行。'));
console.log(chalk.gray(' 需要 Cloudflare 部署?執行 acr initStandard 模式)。\n'));
}
async function initStandard(rl: ReturnType<typeof createInterface>): Promise<void> {
console.log(chalk.gray(' Standard 模式:使用 arcrun.dev 的執行引擎,credential 存在你自己的 CF KV\n'));
@@ -137,7 +157,6 @@ function createCredentialsYamlIfMissing(): void {
// 確保 .gitignore 排除 credentials.yaml
const gitignorePath = join(process.cwd(), '.gitignore');
if (existsSync(gitignorePath)) {
const { readFileSync, appendFileSync } = await import('node:fs');
const content = readFileSync(gitignorePath, 'utf8');
if (!content.includes('credentials.yaml')) {
appendFileSync(gitignorePath, '\ncredentials.yaml\n');