feat: add /register endpoint + fix acr run Mode 1 (inline YAML execution)

- POST /register on cypher.arcrun.dev: HMAC-SHA256(email, ENCRYPTION_KEY) → ak_{32hex}, no DB needed
- acr run: Mode 1 (standard/local) now finds local YAML and POSTs to /cypher/execute inline
- acr init: fix register URL → cypher.arcrun.dev/register; fix local mode description
- acr init --local: creates hello.yaml example workflow
- cli v1.0.3 published

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-16 16:04:14 +08:00
parent ea54aa378b
commit 2594f8371d
5 changed files with 184 additions and 35 deletions
+33 -9
View File
@@ -9,7 +9,7 @@ import { join } from 'node:path';
import chalk from 'chalk';
import { saveConfig, type ArcrunConfig } from '../lib/config.js';
const ARCRUN_REGISTER_URL = 'https://api.arcrun.dev/register';
const ARCRUN_REGISTER_URL = 'https://cypher.arcrun.dev/register';
async function prompt(rl: ReturnType<typeof createInterface>, question: string): Promise<string> {
const answer = await rl.question(chalk.cyan(`? ${question}: `));
@@ -35,21 +35,22 @@ export async function cmdInit(options: { local?: boolean; selfHosted?: boolean }
}
async function initLocal(): Promise<void> {
console.log(chalk.gray(' Local 模式:不需要 Cloudflare 帳號,直接在本機跑 workflow\n'));
console.log(chalk.gray(' Local 模式:不需要 Cloudflare 帳號,workflow 由 arcrun.dev 雲端引擎執行\n'));
const config: ArcrunConfig = {
mode: 'local',
};
saveConfig(config);
createCredentialsYamlIfMissing();
createHelloYamlIfMissing();
console.log(chalk.green('\n ✓ 設定完成 → ~/.arcrun/config.yamllocal 模式)\n'));
console.log(chalk.green('\n ✓ 設定完成 → ~/.arcrun/config.yamllocal 模式)'));
console.log(chalk.green(' ✓ 建立 hello.yaml 範例 workflow\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'));
console.log(chalk.cyan(' acr validate hello.yaml --offline') + ' # 驗證 workflow 格式');
console.log(chalk.cyan(' acr run hello') + ' # 執行 hello workflow\n');
console.log(chalk.gray(' Local 模式YAML 留在本機,workflow 由 arcrun.dev 引擎執行。'));
console.log(chalk.gray(' 需要用自己的 CF 帳號存放 credentials?執行 acr initStandard 模式)。\n'));
}
async function initStandard(rl: ReturnType<typeof createInterface>): Promise<void> {
@@ -79,7 +80,7 @@ async function initStandard(rl: ReturnType<typeof createInterface>): Promise<voi
throw new Error(`API Key 取得失敗(${res.status}):${err}`);
}
const data = await res.json() as { api_key: string; tenant_id: string };
const data = await res.json() as { api_key: string };
apiKey = data.api_key;
console.log(chalk.green(' ✓'));
} catch (e) {
@@ -140,6 +141,29 @@ async function initSelfHosted(rl: ReturnType<typeof createInterface>): Promise<v
console.log(chalk.green(' ✓ 建立 credentials.yaml\n'));
}
function createHelloYamlIfMissing(): void {
const helloPath = join(process.cwd(), 'hello.yaml');
if (!existsSync(helloPath)) {
writeFileSync(helloPath,
'# arcrun hello world workflow\n' +
'# 執行:acr run hello\n\n' +
'name: hello\n' +
'description: "Hello world — 示範如何讓 AI 處理訊息後傳送通知"\n\n' +
'flow:\n' +
' - "input >> ON_SUCCESS >> ai_reply"\n' +
' - "ai_reply >> ON_SUCCESS >> log_output"\n\n' +
'config:\n' +
' ai_reply:\n' +
' component: "component://cmp_openai_chat"\n' +
' model: "gpt-4o-mini"\n' +
' prompt: "請用繁體中文說 Hello World,並解釋 arcrun 是什麼"\n' +
' log_output:\n' +
' component: "component://cmp_log_stdout"\n',
'utf8'
);
}
}
function createCredentialsYamlIfMissing(): void {
const credPath = join(process.cwd(), 'credentials.yaml');
if (!existsSync(credPath)) {