Compare commits

...

1 Commits

Author SHA1 Message Date
xiaoju a222db6f2d fix(workflow): bundle build & register workflow
- Add missing agent deps to template package.json so bun workspace
  resolution works during `bun build` (workflow-agent-cursor for develop,
  workflow-agent-hermes + workflow-execute for solve-issue)
- Create scripts/build-bundles.sh that builds both template bundles into
  dist/*.esm.js with correct --external flags for runtime-symlinked packages
- Add build:bundles script to root package.json
- Extend ensureUncagedWorkflowSymlink to also symlink workflow-util,
  workflow-execute, and workflow-register (needed by externalized bundles)
- Defer agent creation in develop bundle-entry.ts via lazy init so
  requireEnv('WORKFLOW_LLM_API_KEY') only throws at first invocation,
  not at import() time (fixes workflowAsAgent crash)
- Document that env vars must be set before the first worker spawn
  (workers are persistent and don't inherit later env changes)

Fixes #206

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-12 02:58:45 +00:00
13 changed files with 97 additions and 24 deletions
+1
View File
@@ -6,6 +6,7 @@
],
"scripts": {
"build": "bunx tsc --build",
"build:bundles": "bash scripts/build-bundles.sh",
"check": "bunx tsc --build && biome check .",
"typecheck": "bunx tsc --build",
"format": "biome format --write .",
+1
View File
@@ -0,0 +1 @@
workflow-cas
+1
View File
@@ -0,0 +1 @@
workflow-execute
+1
View File
@@ -0,0 +1 @@
workflow-protocol
+1
View File
@@ -0,0 +1 @@
workflow-register
@@ -48,6 +48,9 @@ export async function ensureUncagedWorkflowSymlink(storageRoot: string): Promise
{ name: "workflow-runtime", dir: siblingPackageDir("workflow-runtime") },
{ name: "workflow-cas", dir: siblingPackageDir("workflow-cas") },
{ name: "workflow-protocol", dir: siblingPackageDir("workflow-protocol") },
{ name: "workflow-util", dir: siblingPackageDir("workflow-util") },
{ name: "workflow-execute", dir: siblingPackageDir("workflow-execute") },
{ name: "workflow-register", dir: siblingPackageDir("workflow-register") },
];
for (const pkg of packages) {
+1
View File
@@ -0,0 +1 @@
workflow-runtime
@@ -2,19 +2,17 @@
* develop bundle entry — 小橘 🍊
*
* All roles use cursor-agent with workspace auto-extracted from context.
*
* ENV VARS: WORKFLOW_LLM_API_KEY (required), WORKFLOW_LLM_BASE_URL,
* WORKFLOW_LLM_MODEL, WORKFLOW_CURSOR_MODEL, WORKFLOW_CURSOR_TIMEOUT.
* Must be set before the first worker spawn — workers are persistent and
* do not pick up env changes after initial import.
*/
import { createCursorAgent } from "@uncaged/workflow-agent-cursor";
import type { AgentContext, AgentFn, AgentFnResult } from "@uncaged/workflow-runtime";
import { createWorkflow } from "@uncaged/workflow-runtime";
import { buildDevelopDescriptor, developWorkflowDefinition } from "./src/index.js";
function requireEnv(name: string): string {
const value = process.env[name];
if (value === undefined || value === "") {
throw new Error(`missing required env var: ${name}`);
}
return value;
}
function optionalEnv(name: string): string | null {
const value = process.env[name];
if (value === undefined || value === "") {
@@ -23,23 +21,39 @@ function optionalEnv(name: string): string | null {
return value;
}
const llmProvider = {
baseUrl:
optionalEnv("WORKFLOW_LLM_BASE_URL") ?? "https://dashscope.aliyuncs.com/compatible-mode/v1",
apiKey: requireEnv("WORKFLOW_LLM_API_KEY"),
model: optionalEnv("WORKFLOW_LLM_MODEL") ?? "qwen-plus",
};
function requireEnv(name: string): string {
const value = process.env[name];
if (value === undefined || value === "") {
throw new Error(`missing required env var: ${name}`);
}
return value;
}
const agent = createCursorAgent({
model: optionalEnv("WORKFLOW_CURSOR_MODEL"),
timeout: optionalEnv("WORKFLOW_CURSOR_TIMEOUT")
? Number(optionalEnv("WORKFLOW_CURSOR_TIMEOUT"))
: 0,
workspace: null,
llmProvider,
});
function createLazyAgent(): AgentFn {
let cached: AgentFn | null = null;
return (ctx: AgentContext): Promise<AgentFnResult> => {
if (cached === null) {
const llmProvider = {
baseUrl:
optionalEnv("WORKFLOW_LLM_BASE_URL") ??
"https://dashscope.aliyuncs.com/compatible-mode/v1",
apiKey: requireEnv("WORKFLOW_LLM_API_KEY"),
model: optionalEnv("WORKFLOW_LLM_MODEL") ?? "qwen-plus",
};
cached = createCursorAgent({
model: optionalEnv("WORKFLOW_CURSOR_MODEL"),
timeout: optionalEnv("WORKFLOW_CURSOR_TIMEOUT")
? Number(optionalEnv("WORKFLOW_CURSOR_TIMEOUT"))
: 0,
workspace: null,
llmProvider,
});
}
return cached(ctx);
};
}
const wf = createWorkflow(developWorkflowDefinition, { agent, overrides: null });
const wf = createWorkflow(developWorkflowDefinition, { agent: createLazyAgent(), overrides: null });
export const descriptor = buildDevelopDescriptor();
export const run = wf;
@@ -12,6 +12,7 @@
"test": "bun test"
},
"dependencies": {
"@uncaged/workflow-agent-cursor": "workspace:*",
"@uncaged/workflow-register": "workspace:*",
"@uncaged/workflow-runtime": "workspace:*",
"zod": "^4.0.0"
@@ -3,6 +3,10 @@
*
* preparer + submitter → hermes agent
* developer → workflow-as-agent (delegates to "develop" workflow)
*
* ENV VARS: WORKFLOW_HERMES_MODEL, WORKFLOW_HERMES_TIMEOUT.
* Must be set before the first worker spawn — workers are persistent and
* do not pick up env changes after initial import.
*/
import { createHermesAgent } from "@uncaged/workflow-agent-hermes";
import { workflowAsAgent } from "@uncaged/workflow-execute";
@@ -12,13 +12,14 @@
"test": "bun test"
},
"dependencies": {
"@uncaged/workflow-agent-hermes": "workspace:*",
"@uncaged/workflow-execute": "workspace:*",
"@uncaged/workflow-register": "workspace:*",
"@uncaged/workflow-runtime": "workspace:*",
"zod": "^4.0.0"
},
"devDependencies": {
"@uncaged/workflow-cas": "workspace:*",
"@uncaged/workflow-execute": "workspace:*",
"@uncaged/workflow-protocol": "workspace:*"
}
}
+1
View File
@@ -0,0 +1 @@
workflow-util
+43
View File
@@ -0,0 +1,43 @@
#!/usr/bin/env bash
set -euo pipefail
# Packages externalized from bundles — resolved at runtime via symlinks
# created by ensureUncagedWorkflowSymlink in workflow-register.
EXTERNAL=(
--external @uncaged/workflow-runtime
--external @uncaged/workflow-protocol
--external @uncaged/workflow-cas
--external @uncaged/workflow-util
--external @uncaged/workflow-execute
--external @uncaged/workflow-register
)
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$REPO_ROOT"
mkdir -p dist
echo "Building develop bundle..."
bun build packages/workflow-template-develop/bundle-entry.ts \
--bundle --format esm --target bun \
"${EXTERNAL[@]}" \
--outfile dist/develop.esm.js
echo "Building solve-issue bundle..."
bun build packages/workflow-template-solve-issue/bundle-entry.ts \
--bundle --format esm --target bun \
"${EXTERNAL[@]}" \
--outfile dist/solve-issue.esm.js
echo "Done. Bundles written to dist/"
# Register bundles if --register flag is passed
if [[ "${1:-}" == "--register" ]]; then
STORAGE_ROOT="${UNCAGED_WORKFLOW_STORAGE_ROOT:-${WORKFLOW_STORAGE_ROOT:-$HOME/.uncaged/workflow}}"
echo "Registering bundles..."
for bundle in develop solve-issue; do
cp "dist/${bundle}.esm.js" "$STORAGE_ROOT/${bundle}.esm.js"
uncaged-workflow workflow add "$bundle" "$STORAGE_ROOT/${bundle}.esm.js"
done
echo "Bundles registered."
fi