feat(exposure): 完全移除 acr push 暴露 consent 閘 (Arcrun#13 P1)

leo 2026-06-29 拍板:arcrun 是給 AI 用的系統,push/暴露不再需要人類確認。
- 刪 cypher-executor/src/lib/exposure-consent.ts(server 閘,MCP push 的真正擋點)
- 刪 cli/src/lib/exposure-warning.ts(CLI 互動 + 非 TTY 拒絕)
- recipes.ts / webhooks-named.ts:移除 checkExposureConsent 403 閘,直接放行
- recipe.ts / push.ts:移除 obtainExposureConsent 呼叫,不再 prompt/拒絕
- init-seed / seed-api-recipes:移除種子層級 consent
- exposure_consent 欄位降為向後相容(讀舊 record 不報錯,不再寫入/檢查)
不補審計線索、不做替代防護(leo:先拿掉,出問題再設置)。
tsc 全綠(cypher-executor + cli)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
uncle6me-web
2026-06-29 20:58:32 +08:00
parent 43948d9247
commit c1a06df68f
9 changed files with 20 additions and 280 deletions
+2 -13
View File
@@ -11,7 +11,6 @@ import chalk from 'chalk';
import ora from 'ora';
import { loadConfig, getCypherExecutorUrl } from '../lib/config.js';
import { loadWorkflowYaml, parseTriplets, validateRelations } from '../lib/yaml-parser.js';
import { obtainExposureConsent } from '../lib/exposure-warning.js';
export async function cmdPush(filePath: string): Promise<void> {
const config = loadConfig();
@@ -96,17 +95,8 @@ export async function cmdPush(filePath: string): Promise<void> {
process.exit(1);
}
// 資料外流警示:部署 webhook = 把 workflow 變對外可呼叫 endpoint(暴露面)。
// 已同意過(本機記住)→ 回非 null 自動放行;未同意/取消/非互動 → null → 中止
// server 端獨立存法律憑證並強制(防 CLI 被繞過)。
const consent = await obtainExposureConsent({
kind: 'workflow',
resourceName: workflow.name,
destination: `${executorUrl}/webhooks/named/${workflow.name}/trigger`,
});
if (!consent) {
process.exit(1);
}
// 暴露 consent 閘已移除(leo 2026-06-29Arcrun#13):arcrun 是給 AI 用的系統,
// push/暴露不再需要人類確認,AI/MCP 隨時可部署。暴露風險由用戶自負(同 n8n 建 webhook
// POST 至 /webhooks/named
const deploySpinner = ora(`部署 "${workflow.name}" 至 ${executorUrl}`).start();
@@ -119,7 +109,6 @@ export async function cmdPush(filePath: string): Promise<void> {
graph,
config: workflow.config ?? {},
description: workflow.description ?? '',
exposure_consent: consent ?? undefined,
}),
});
+4 -26
View File
@@ -7,7 +7,6 @@ import chalk from 'chalk';
import ora from 'ora';
import { readFileSync, existsSync } from 'node:fs';
import { loadConfig, getCypherExecutorUrl, DEFAULT_PUBLIC_LIBRARY_URL } from '../lib/config.js';
import { obtainExposureConsent } from '../lib/exposure-warning.js';
import yaml from 'js-yaml';
interface RecipeYaml {
@@ -70,16 +69,7 @@ export async function cmdRecipePush(filePath: string): Promise<void> {
const executorUrl = getCypherExecutorUrl(config);
// 資料外流警示:recipe 定義一個資料去向(endpoint)。首次 push 需人類明示同意(公私一視同仁)
// 已同意過(本機記住)→ 回非 null 自動放行;未同意/取消/非互動 → null → 中止。
const consent = await obtainExposureConsent({
kind: 'recipe',
resourceName: recipe.canonical_id,
destination: recipe.endpoint,
});
if (!consent) {
process.exit(1);
}
// 暴露 consent 閘已移除(leo 2026-06-29Arcrun#13):recipe push 不再需要人類確認
const spinner = ora(`上傳 recipe "${recipe.canonical_id}"`).start();
@@ -90,7 +80,7 @@ export async function cmdRecipePush(filePath: string): Promise<void> {
'Content-Type': 'application/json',
'X-Arcrun-API-Key': config.api_key,
},
body: JSON.stringify({ ...recipe, exposure_consent: consent ?? undefined }),
body: JSON.stringify(recipe),
});
const data = await res.json() as { success: boolean; recipe?: RecipeDefinition; error?: string };
@@ -307,7 +297,7 @@ export async function cmdRecipePull(canonicalId: string, author?: string): Promi
return;
}
// 2. 寫進自己私庫(POST /recipes,帶 derived_from 溯源 + 種子級同意:pull 公庫公共資料非新暴露)。
// 2. 寫進自己私庫(POST /recipes,帶 derived_from 溯源)。
const r = pub.recipe;
const executorUrl = getCypherExecutorUrl(config);
const installRes = await fetch(`${executorUrl}/recipes`, {
@@ -316,11 +306,6 @@ export async function cmdRecipePull(canonicalId: string, author?: string): Promi
body: JSON.stringify({
...r,
derived_from: r.uuid, // 溯源:私庫這份來自公庫哪個 uuid
exposure_consent: {
confirmed_by_human: true,
understood: `pull from public library: ${canonicalId}`,
confirmed_at: new Date().toISOString(),
},
}),
});
const inst = await installRes.json() as { success: boolean; recipe?: RecipeDefinition; error?: string };
@@ -355,13 +340,7 @@ export async function cmdRecipeSubmitP(canonicalId: string, author?: string): Pr
process.exit(1);
}
// 2. 投稿到公庫 = 暴露面 → 取得人類明示同意(mindset §6
const consent = await obtainExposureConsent({
kind: 'recipe',
resourceName: canonicalId,
destination: `公庫(${DEFAULT_PUBLIC_LIBRARY_URL}`,
});
if (!consent) process.exit(1);
// 暴露 consent 閘已移除(leo 2026-06-29Arcrun#13):投稿公庫不再需要人類確認
const spinner = ora(`投稿 recipe「${canonicalId}」到公庫`).start();
try {
@@ -373,7 +352,6 @@ export async function cmdRecipeSubmitP(canonicalId: string, author?: string): Pr
author: author ?? my.recipe.author,
derived_from: my.recipe.derived_from ?? my.recipe.uuid,
submitter: author ?? config.api_key,
exposure_consent: consent,
}),
});
const data = await res.json() as { success: boolean; recipe?: { uuid?: string; author?: string }; error?: string };