From 004d0079e83a3bb4fb8ceb75208c3ed86fc24eef Mon Sep 17 00:00:00 2001 From: pikastech Date: Mon, 20 Jul 2026 11:37:37 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20=E6=94=AF=E6=8C=81=20AgentRun=20L1=20?= =?UTF-8?q?=E8=B5=84=E6=BA=90=E6=9F=A5=E8=AF=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agents/skills/unidesk-agentrun/SKILL.md | 1 + scripts/src/agentrun/config.ts | 21 +++++++++++++ scripts/src/agentrun/entry.ts | 12 +++---- scripts/src/agentrun/resource-actions.ts | 40 ++++++++++++++++-------- scripts/src/agentrun/rest-bridge.ts | 13 +++++++- scripts/src/agentrun/utils.ts | 1 + 6 files changed, 68 insertions(+), 20 deletions(-) diff --git a/.agents/skills/unidesk-agentrun/SKILL.md b/.agents/skills/unidesk-agentrun/SKILL.md index 79921a42..e0720113 100644 --- a/.agents/skills/unidesk-agentrun/SKILL.md +++ b/.agents/skills/unidesk-agentrun/SKILL.md @@ -52,6 +52,7 @@ bun scripts/cli.ts agentrun native-development manager start bun scripts/cli.ts agentrun native-development manager status bun scripts/cli.ts agentrun native-development manager logs bun scripts/cli.ts agentrun native-development manager restart +bun scripts/cli.ts agentrun result run/ --command --native-development ``` - 配置唯一来源是 `config/agentrun.yaml#nativeDevelopment.manager`; diff --git a/scripts/src/agentrun/config.ts b/scripts/src/agentrun/config.ts index 775ec044..8403adf9 100644 --- a/scripts/src/agentrun/config.ts +++ b/scripts/src/agentrun/config.ts @@ -53,6 +53,27 @@ export function readAgentRunClientConfig(env: NodeJS.ProcessEnv = process.env): } } +export function readAgentRunNativeDevelopmentClientConfig(env: NodeJS.ProcessEnv = process.env): AgentRunClientConfig { + const config = readAgentRunClientConfig(env); + const input = record(Bun.YAML.parse(readFileSync(config.sourcePath, "utf8")) as unknown); + const nativeDevelopment = record(input.nativeDevelopment); + const manager = record(nativeDevelopment.manager); + const probeHost = stringFieldFromRecord(manager, "probeHost", "nativeDevelopment.manager"); + const port = numberFieldFromRecord(manager, "port", "nativeDevelopment.manager", { min: 1, max: 65_535 }); + const apiKeySourceRef = stringFieldFromRecord(manager, "apiKeySourceRef", "nativeDevelopment.manager"); + const apiKeySourceKey = stringFieldFromRecord(manager, "apiKeySourceKey", "nativeDevelopment.manager"); + if (apiKeySourceRef.includes("..")) throw new Error("nativeDevelopment.manager.apiKeySourceRef must not contain .."); + const apiKeyFile = apiKeySourceRef.startsWith("/") + ? apiKeySourceRef + : join("/root/.unidesk/.state/secrets", ...apiKeySourceRef.split("/")); + return { + ...config, + manager: { ...config.manager, baseUrl: `http://${probeHost}:${port}/` }, + auth: { ...config.auth, env: apiKeySourceKey, file: apiKeyFile }, + publicExposure: null, + }; +} + export function parseAgentRunClientConfigYaml(raw: string, sourcePath = "config/agentrun.yaml"): AgentRunClientConfig { const input = record(Bun.YAML.parse(raw) as unknown); validateAgentRunConfigEnvelope(input, sourcePath); diff --git a/scripts/src/agentrun/entry.ts b/scripts/src/agentrun/entry.ts index 3f95e479..411562cb 100644 --- a/scripts/src/agentrun/entry.ts +++ b/scripts/src/agentrun/entry.ts @@ -42,7 +42,7 @@ import { agentRunExplain, isRecord, parseGitMirrorStatusOptions, parseStatusOpti import { renderAgentRunControlPlaneActionSummary, renderAgentRunControlPlanePlanSummary, renderAgentRunControlPlaneStatusSummary } from "./public-exposure"; import { providerProfileHelp, runProviderProfileCommand } from "./provider-profile"; import { renderedCliResult } from "./render"; -import { agentRunGetKindHelp, runAgentRunResourceCommand } from "./resource-actions"; +import { AGENTRUN_RESOURCE_TRANSPORT_SCOPE, agentRunGetKindHelp, runAgentRunResourceCommand } from "./resource-actions"; import { runAgentRunRestCompatCommand, runGitMirrorJob, startAsyncAgentRunJob } from "./rest-bridge"; import { renderAgentRunSecretSyncResult } from "./secret-sync-output"; import { exposeAgentRun, restartYamlLane, secretSync, triggerCurrent } from "./trigger"; @@ -269,7 +269,7 @@ export function agentRunHelpText(args: string[]): string { if (verb === "get" && kind !== undefined) return agentRunGetKindHelp(kind); if (verb === "get") { return [ - "Usage: bun scripts/cli.ts agentrun get [options]", + `Usage: bun scripts/cli.ts agentrun get [options] ${AGENTRUN_RESOURCE_TRANSPORT_SCOPE}`, "", "Resources: tasks|attempts|sessions|runs|commands|runnerjobs|aipodspecs", "Output: table by default; use -o wide|name|json|yaml.", @@ -296,18 +296,18 @@ export function agentRunHelpText(args: string[]): string { } if (verb === "events") { return [ - "Usage: bun scripts/cli.ts agentrun events run/ [--after-seq N] [--expect N [--timeout 120s]] [--limit 100] [-o json|yaml] [--full|--raw]", - " bun scripts/cli.ts agentrun events run/ --detail-seq [-o json|yaml]", + `Usage: bun scripts/cli.ts agentrun events run/ [--after-seq N] [--expect N [--timeout 120s]] [--limit 100] [-o json|yaml] [--full|--raw] ${AGENTRUN_RESOURCE_TRANSPORT_SCOPE}`, + ` bun scripts/cli.ts agentrun events run/ --detail-seq [-o json|yaml] ${AGENTRUN_RESOURCE_TRANSPORT_SCOPE}`, "", "--expect waits in one manager-held request until N new events, target completion, or timeout; --timeout defaults to 120s. --limit remains an independent output budget.", "--detail-seq returns one bounded Secret-safe EventDetail projection. Use the separate --after-seq --limit 1 --full|--raw path only for explicit complete disclosure.", ].join("\n"); } if (verb === "logs") { - return "Usage: bun scripts/cli.ts agentrun logs session/ [--tail 100|--after-seq N] [--limit 100] [--full-text] [-o json|yaml] [--raw]"; + return `Usage: bun scripts/cli.ts agentrun logs session/ [--tail 100|--after-seq N] [--limit 100] [--full-text] [-o json|yaml] [--raw] ${AGENTRUN_RESOURCE_TRANSPORT_SCOPE}`; } if (verb === "result") { - return "Usage: bun scripts/cli.ts agentrun result run/ --command | agentrun result command/ --run "; + return `Usage: bun scripts/cli.ts agentrun result run/ --command ${AGENTRUN_RESOURCE_TRANSPORT_SCOPE}\n bun scripts/cli.ts agentrun result command/ --run ${AGENTRUN_RESOURCE_TRANSPORT_SCOPE}`; } if (verb === "ack") { return "Usage: bun scripts/cli.ts agentrun ack task/|session/ [--reader-id cli]"; diff --git a/scripts/src/agentrun/resource-actions.ts b/scripts/src/agentrun/resource-actions.ts index 268afda8..0579ea44 100644 --- a/scripts/src/agentrun/resource-actions.ts +++ b/scripts/src/agentrun/resource-actions.ts @@ -34,27 +34,29 @@ import { import { sha256Fingerprint } from "../platform-infra-ops-library"; import type { AgentRunResourceOptions, AgentRunResourceRef, AgentRunResourceVerb } from "./utils"; -import { agentRunDryRunPlan } from "./config"; +import { agentRunDryRunPlan, readAgentRunNativeDevelopmentClientConfig } from "./config"; import { renderEventDetail } from "./event-detail"; import { isHelpArg, renderAgentRunHelp } from "./entry"; import { agentRunExplain, arrayRecords, shortId } from "./options"; import { agentRunCancelAuthorityDisclosure, agentRunCancelCascadeScope, agentRunCancelFencingDisclosure, agentRunCancelRunnerAbortDisclosure, compactAipodSpecDescriptionPayload, compactTaskDescriptionPayload, innerData, normalizeAipodSpecItems, normalizeAttemptResources, normalizeEventItems, normalizeLogItems, normalizeQueueAttemptItems, normalizeRunnerJobItems, normalizeSessionItems, normalizeSingleCommand, normalizeTaskItems, parseResourceKind, parseResourceRef, renderAipodSpecDescription, renderDescribe, renderEventLike, renderGenericDescription, renderMachine, renderMutationSummary, renderQueueAttemptList, renderQueueRetrySummary, renderResourceResult, renderResultSummary, renderTaskDescription, renderedCliResult, requiredContext, rerunWithoutDryRun, resolveAgentRunCancelPolicyTarget, resourceApply, resourceCreate, resourceDispatch, resourceSessionPromptCommand, taskInputDescriptionPayload, taskListState, unwrapTaskDetail } from "./render"; -import { AgentRunRestError, renderAgentRunRestError, resolveAgentRunRestTarget, runAgentRunRestCommand, withAgentRunRestTarget } from "./rest-bridge"; +import { AgentRunRestError, renderAgentRunRestError, resolveAgentRunRestTarget, runAgentRunRestCommand, withAgentRunNativeClientConfig, withAgentRunRestTarget } from "./rest-bridge"; import { renderSessionSendError } from "./session-send-render"; import { prepareAgentRunTargetTaskPreflight } from "./target-execution"; import { runnerJobObservationScript, type RunnerJobObservationIdentity } from "./runner-observation"; import { isChineseTaskTitle, validateChineseTaskTitle, withAgentRunTargetTaskPreflight } from "./target-task"; import { capture, captureJsonPayload, compactCapture, nonNegativeIntegerOrNull, record, stringOrNull } from "./utils"; +export const AGENTRUN_RESOURCE_TRANSPORT_SCOPE = "[--native-development | [--target |--node ] [--lane ]]"; + export function agentRunGetKindHelp(kindRaw: string): string { const kind = parseResourceKind(kindRaw); - if (kind === "task") return "Usage: bun scripts/cli.ts agentrun get tasks [--queue commander] [--state running,pending,completed,failed] [--unread] [--limit 20] [-o wide|name|json|yaml]"; - if (kind === "attempt") return "Usage: bun scripts/cli.ts agentrun get attempts --task [--cursor ] [--limit 20] [--full] [-o json|yaml] [--raw]"; - if (kind === "session") return "Usage: bun scripts/cli.ts agentrun get sessions [--limit 20] [-o wide|name|json|yaml]"; - if (kind === "run") return "Usage: bun scripts/cli.ts agentrun get runs --task [--limit 20] [-o wide|name|json|yaml]"; - if (kind === "command") return "Usage: bun scripts/cli.ts agentrun get commands --run [--command ] [-o wide|name|json|yaml]"; - if (kind === "runnerjob") return "Usage: bun scripts/cli.ts agentrun get runnerjobs --run --command [-o wide|name|json|yaml]"; - if (kind === "aipodspec") return "Usage: bun scripts/cli.ts agentrun get aipodspecs [-o wide|name|json|yaml]"; + if (kind === "task") return `Usage: bun scripts/cli.ts agentrun get tasks [--queue commander] [--state running,pending,completed,failed] [--unread] [--limit 20] [-o wide|name|json|yaml] ${AGENTRUN_RESOURCE_TRANSPORT_SCOPE}`; + if (kind === "attempt") return `Usage: bun scripts/cli.ts agentrun get attempts --task [--cursor ] [--limit 20] [--full] [-o json|yaml] [--raw] ${AGENTRUN_RESOURCE_TRANSPORT_SCOPE}`; + if (kind === "session") return `Usage: bun scripts/cli.ts agentrun get sessions [--limit 20] [-o wide|name|json|yaml] ${AGENTRUN_RESOURCE_TRANSPORT_SCOPE}`; + if (kind === "run") return `Usage: bun scripts/cli.ts agentrun get runs --task [--limit 20] [-o wide|name|json|yaml] ${AGENTRUN_RESOURCE_TRANSPORT_SCOPE}`; + if (kind === "command") return `Usage: bun scripts/cli.ts agentrun get commands --run [--command ] [-o wide|name|json|yaml] ${AGENTRUN_RESOURCE_TRANSPORT_SCOPE}`; + if (kind === "runnerjob") return `Usage: bun scripts/cli.ts agentrun get runnerjobs --run --command [-o wide|name|json|yaml] ${AGENTRUN_RESOURCE_TRANSPORT_SCOPE}`; + if (kind === "aipodspec") return `Usage: bun scripts/cli.ts agentrun get aipodspecs [-o wide|name|json|yaml] ${AGENTRUN_RESOURCE_TRANSPORT_SCOPE}`; return "Unknown resource. Supported: tasks, attempts, sessions, runs, commands, runnerjobs, aipodspecs."; } @@ -76,7 +78,9 @@ export async function runAgentRunResourceCommand(config: UniDeskConfig | null, v const bridgeActionArgs = stripAgentRunResourceWrapperArgs(actionArgs); try { const targetTaskPreflight = await prepareAgentRunTargetTaskPreflight(config, canonicalArgs, options); - return await withAgentRunTargetTaskPreflight(targetTaskPreflight, async () => await withAgentRunRestTarget(resolveAgentRunRestTarget(config, options), async () => { + const nativeClientConfig = options.nativeDevelopment ? readAgentRunNativeDevelopmentClientConfig() : null; + const restTarget = resolveAgentRunRestTarget(options.nativeDevelopment ? null : config, options); + return await withAgentRunTargetTaskPreflight(targetTaskPreflight, async () => await withAgentRunNativeClientConfig(nativeClientConfig, async () => await withAgentRunRestTarget(restTarget, async () => { if (verb === "explain") return renderedCliResult(true, command, agentRunExplain(action ?? "task", resourceArgs, options)); if (verb === "get") return await resourceGet(config, command, action, bridgeActionArgs, options); if (verb === "describe") return await resourceDescribe(config, command, action, bridgeActionArgs, options); @@ -91,7 +95,7 @@ export async function runAgentRunResourceCommand(config: UniDeskConfig | null, v if (verb === "apply") return await resourceApply(config, command, bridgeActionArgs, options); if (verb === "send") return await resourceSessionPromptCommand(config, command, action, bridgeActionArgs, options); return renderedCliResult(false, command, `Unsupported AgentRun resource command. Try: bun scripts/cli.ts agentrun --help`); - })); + }))); } catch (error) { if (error instanceof AgentRunRestError) { if (verb === "send") return renderSessionSendError(command, error.toPayload(command), options); @@ -157,7 +161,7 @@ export function stripAgentRunResourceWrapperArgs(args: string[]): string[] { } if (arg.startsWith("--output=") || arg.startsWith("-o=")) continue; if (arg.startsWith("--node=") || arg.startsWith("--lane=")) continue; - if (arg === "--full" || arg === "--raw" || arg === "--debug" || arg === "--full-text" || arg === "--input") continue; + if (arg === "--full" || arg === "--raw" || arg === "--debug" || arg === "--full-text" || arg === "--input" || arg === "--native-development") continue; result.push(arg); } return result; @@ -193,6 +197,7 @@ export function parseResourceOptions(args: string[]): AgentRunResourceOptions { title: null, idempotencyKey: null, promptStdin: false, + nativeDevelopment: false, node: null, lane: null, target: null, @@ -203,7 +208,7 @@ export function parseResourceOptions(args: string[]): AgentRunResourceOptions { passthroughArgs: [], }; const valueFlags = new Set(["-o", "--output", "--limit", "--cursor", "--queue", "--state", "--reader-id", "--task", "--task-id", "--run", "--run-id", "--command", "--command-id", "--session", "--session-id", "--after-seq", "--expect", "--timeout", "--detail-seq", "--tail", "--reason", "-f", "--file", "--filename", "--aipod", "--title", "--idempotency-key", "--node", "--lane", "--target", "--target-workspace", "--repo", "--ref", "--mdtodo-id"]); - const booleanFlags = new Set(["--full", "--raw", "--debug", "--input", "--unread", "--dry-run", "--full-text", "--prompt-stdin", "--stdin"]); + const booleanFlags = new Set(["--full", "--raw", "--debug", "--input", "--unread", "--dry-run", "--full-text", "--prompt-stdin", "--stdin", "--native-development"]); for (let index = 0; index < args.length; index += 1) { const arg = args[index] ?? ""; if (!arg.startsWith("-")) continue; @@ -284,6 +289,14 @@ export function parseResourceOptions(args: string[]): AgentRunResourceOptions { } function validateResourceOptionsForVerb(verb: AgentRunResourceVerb, options: AgentRunResourceOptions): void { + if (options.nativeDevelopment) { + if (options.node !== null || options.lane !== null || options.target !== null) { + throw new AgentRunRestError("validation-failed", "--native-development cannot be combined with --target, --node, or --lane"); + } + if (!new Set(["get", "describe", "events", "logs", "result"]).has(verb)) { + throw new AgentRunRestError("validation-failed", "--native-development is read-only and supports get, describe, events, logs, and result"); + } + } if (options.eventDetailSeq !== null && verb !== "events") { throw new AgentRunRestError( "validation-failed", @@ -328,6 +341,7 @@ export function applyResourceOption(options: AgentRunResourceOptions, flag: stri else if (flag === "--dry-run") options.dryRun = true; else if (flag === "--full-text") options.fullText = true; else if (flag === "--prompt-stdin" || flag === "--stdin") options.promptStdin = true; + else if (flag === "--native-development") options.nativeDevelopment = true; else if (flag === "--limit") options.limit = parseNonNegativeInt(value, "--limit", 20, 500); else if (flag === "--cursor") options.cursor = parseNonNegativeInt(value, "--cursor", 0, Number.MAX_SAFE_INTEGER); else if (flag === "--queue") options.queue = requiredValue(value, flag); diff --git a/scripts/src/agentrun/rest-bridge.ts b/scripts/src/agentrun/rest-bridge.ts index 0db792cc..8629317d 100644 --- a/scripts/src/agentrun/rest-bridge.ts +++ b/scripts/src/agentrun/rest-bridge.ts @@ -1220,7 +1220,7 @@ export async function sessionRunnerJobBody(args: string[], defaults: Record> { - const clientConfig = readAgentRunClientConfig(); + const clientConfig = activeAgentRunNativeClientConfig ?? readAgentRunClientConfig(); const effectiveTimeoutMs = Math.max(clientConfig.manager.timeoutMs, requestTimeoutMs ?? 0); if (activeAgentRunRestTarget !== null) return await agentRunLaneRestRequest(command, method, pathValue, body, clientConfig, activeAgentRunRestTarget, effectiveTimeoutMs); const auth = resolveAgentRunAuth(clientConfig); @@ -1409,6 +1409,16 @@ export async function withAgentRunRestTarget(target: AgentRunRestTarget | nul } } +export async function withAgentRunNativeClientConfig(config: AgentRunClientConfig | null, action: () => Promise): Promise { + const previous = activeAgentRunNativeClientConfig; + activeAgentRunNativeClientConfig = config; + try { + return await action(); + } finally { + activeAgentRunNativeClientConfig = previous; + } +} + export function renderAgentRunRestError(command: string, error: AgentRunRestError, options: AgentRunResourceOptions): RenderedCliResult { const rawPayload = error.toPayload(command); if (options.raw) return renderMachine(command, rawPayload, "json", false); @@ -1500,3 +1510,4 @@ export class AgentRunRestError extends Error { } export let activeAgentRunRestTarget: AgentRunRestTarget | null = null; +export let activeAgentRunNativeClientConfig: AgentRunClientConfig | null = null; diff --git a/scripts/src/agentrun/utils.ts b/scripts/src/agentrun/utils.ts index 815d7ccf..4f9d5ebd 100644 --- a/scripts/src/agentrun/utils.ts +++ b/scripts/src/agentrun/utils.ts @@ -88,6 +88,7 @@ export interface AgentRunResourceOptions { title: string | null; idempotencyKey: string | null; promptStdin: boolean; + nativeDevelopment: boolean; node: string | null; lane: string | null; target: string | null;