fix: remove direct helper workspaceFiles injection

This commit is contained in:
Codex Agent
2026-06-08 12:19:31 +08:00
parent fb09510d63
commit 6b175ea28a
2 changed files with 61 additions and 28 deletions
@@ -0,0 +1,27 @@
import assert from "node:assert/strict";
import test from "node:test";
import { createAgentChatRequestBody, findRemovedWorkspaceFileInput } from "./client.ts";
test("direct helper agent chat body never sends legacy workspaceFiles", () => {
const body = createAgentChatRequestBody({
message: "validate gitbundle direct helper payload",
providerProfile: "deepseek",
sessionId: "ses_test",
conversationId: "cnv_test",
projectId: "prj_test",
traceId: "trc_test"
});
assert.equal(body.shortConnection, true);
assert.equal(Object.hasOwn(body, "workspaceFiles"), false);
assert.equal(Object.hasOwn(body, "resourceWorkspaceFiles"), false);
});
test("direct helper rejects removed spec injection inputs", () => {
assert.equal(findRemovedWorkspaceFileInput({ "spec-path": ".hwlab/hwpod-spec.yaml" }, {}), "--spec-path");
assert.equal(findRemovedWorkspaceFileInput({ specPath: ".hwlab/hwpod-spec.yaml" }, {}), "--specPath");
assert.equal(findRemovedWorkspaceFileInput({}, { HWPOD_SPEC_CONTENT: "kind: Hwpod" }), "HWPOD_SPEC_CONTENT");
assert.equal(findRemovedWorkspaceFileInput({}, { HWPOD_SPEC: "kind: Hwpod" }), "HWPOD_SPEC");
assert.equal(findRemovedWorkspaceFileInput({}, {}), null);
});
+34 -28
View File
@@ -4,7 +4,6 @@
* 遵循 cli-spec:配置从 config.json 单一来源
*/
import { readFileSync } from "node:fs";
import { existsSync } from "node:fs";
import path from "node:path";
// ---- config ----
@@ -45,35 +44,39 @@ function resolveProviderProfile(args: Record<string, string | undefined>) {
fail("provider_profile_required", `spawn requires --profile or ${INHERITED_PROVIDER_PROFILE_ENV}`);
}
// ---- workspace files auto-inheritance ----
type WorkspaceFile = { path: string; content: string; encoding: "utf8" };
const REMOVED_WORKSPACE_FILE_ARGS = new Map([
["specPath", "--specPath"],
["spec-path", "--spec-path"]
]);
const REMOVED_WORKSPACE_FILE_ENV = ["HWPOD_SPEC_CONTENT", "HWPOD_SPEC"];
function collectWorkspaceFiles(args: Record<string, string | undefined>): WorkspaceFile[] {
const files: WorkspaceFile[] = [];
const specPath = args.specPath;
// 1. explicit --spec-path
if (specPath) {
if (!existsSync(specPath)) throw new Error(`spec file not found: ${specPath}`);
files.push({ path: ".hwlab/hwpod-spec.yaml", content: readFileSync(specPath, "utf-8"), encoding: "utf8" });
export function findRemovedWorkspaceFileInput(args: Record<string, string | undefined>, env: Record<string, string | undefined> = process.env): string | null {
for (const [key, label] of REMOVED_WORKSPACE_FILE_ARGS.entries()) {
if (textValue(args[key])) return label;
}
// 2. auto-detect: .hwlab/hwpod-spec.yaml in CWD (casrun-injected)
const cwdSpec = ".hwlab/hwpod-spec.yaml";
if (!specPath && existsSync(cwdSpec)) {
files.push({ path: cwdSpec, content: readFileSync(cwdSpec, "utf-8"), encoding: "utf8" });
for (const name of REMOVED_WORKSPACE_FILE_ENV) {
if (textValue(env[name])) return name;
}
return null;
}
// 3. env var HWPOD_SPEC_CONTENT (plain yaml or base64)
const envSpec = process.env.HWPOD_SPEC_CONTENT || process.env.HWPOD_SPEC;
if (!specPath && !existsSync(cwdSpec) && envSpec) {
const content = envSpec.startsWith("eyJ") || envSpec.startsWith("YXBp")
? Buffer.from(envSpec, "base64").toString("utf-8")
: envSpec;
files.push({ path: ".hwlab/hwpod-spec.yaml", content, encoding: "utf8" });
}
return files;
export function createAgentChatRequestBody(input: {
message: string;
providerProfile: string;
sessionId: string;
conversationId: string;
projectId: string;
traceId: string;
}): Record<string, unknown> {
return {
message: input.message,
providerProfile: input.providerProfile,
sessionId: input.sessionId,
conversationId: input.conversationId,
projectId: input.projectId,
traceId: input.traceId,
shortConnection: true
};
}
function apiKey(): string {
@@ -115,6 +118,10 @@ export async function spawn(args: Record<string, string | undefined>): Promise<v
const projectId = args.projectId || cfg.defaultProjectId;
const message = args.message || (args.messageFile ? readFileSync(args.messageFile, "utf-8") : null);
if (!message) fail("message_required", "--message or --message-file required");
const removedWorkspaceFileInput = findRemovedWorkspaceFileInput(args);
if (removedWorkspaceFileInput) {
fail("legacy_workspace_files_removed", `${removedWorkspaceFileInput} was removed; use AgentRun ResourceBundleRef kind=gitbundle with bundles[]`);
}
// session
let sessionId = args.sessionId || "";
@@ -133,12 +140,11 @@ export async function spawn(args: Record<string, string | undefined>): Promise<v
if (!conversationId) conversationId = `cnv_cli_${Date.now()}`;
// submit
const workspaceFiles = collectWorkspaceFiles(args);
const traceId = `trc_${profile}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
const r = await fetch(`${cfg.apiBaseUrl}/v1/agent/chat`, {
method: "POST",
headers: { ...authHeaders(), "prefer": "respond-async", "x-trace-id": traceId, "x-hwlab-short-connection": "1" },
body: JSON.stringify({ message, providerProfile: profile, sessionId, conversationId, projectId, traceId, shortConnection: true, ...(workspaceFiles.length > 0 ? { workspaceFiles } : {}) })
body: JSON.stringify(createAgentChatRequestBody({ message, providerProfile: profile, sessionId, conversationId, projectId, traceId }))
});
const b = await r.json();
out({