Compare commits

..

1 Commits

Author SHA1 Message Date
xiaoju 6d4bf108bb fix(publish-all): regenerate lockfile before pack
After bumping versions, bun pm pack reads the old bun.lock and resolves
workspace:* to stale versions. Now deletes bun.lock and runs bun install
before the pack loop to ensure correct resolution.

小橘 <xiaoju@shazhou.work>
2026-05-13 03:52:10 +00:00
2 changed files with 26 additions and 86 deletions
+16 -86
View File
@@ -183,63 +183,35 @@ How to build, test, and publish workflow bundles for uncaged-workflow.
A workflow bundle is a single ESM file (\`.esm.js\`) that exports:
\`\`\`typescript
// Required named exports (no default export)
// Required exports
export const descriptor: WorkflowDescriptor;
export const run: WorkflowFn;
export const run: WorkflowRun;
\`\`\`
## WorkflowDescriptor
Serialized metadata for the registry. Every role must include both \`description\` and \`schema\` (JSON Schema object). The graph uses an edges array where each edge has \`from\`, \`to\`, and \`condition\`.
Serialized metadata for the registry (per-role JSON Schema plus a static routing graph):
\`\`\`typescript
type WorkflowDescriptor = {
description: string;
roles: Record<string, {
description: string;
schema: object; // JSON Schema — use z.toJSONSchema(zodSchema) to generate
}>;
roles: Record<string, { description: string; schema: unknown /* JSON Schema */ }>;
graph: {
edges: Array<{
from: string; // role name, or "__start__"
to: string; // role name, or "__end__"
condition: string; // e.g. "FALLBACK"
conditionDescription?: string | null;
from: string;
to: string;
condition: string;
conditionDescription: string | null;
}>;
};
};
\`\`\`
**descriptor is static data** — it is read at \`workflow add\` (register) time via \`import()\`. It must NOT trigger any side effects or read environment variables.
## WorkflowFn
## WorkflowRun
Async generator from \`createWorkflow(definition, binding)\` (**@uncaged/workflow-runtime**) — yields each role output until the workflow completes.
## ModeratorTable
Declarative routing table. Transitions use the \`role\` field (not \`next\`):
\`\`\`typescript
import { START, END, type ModeratorTable } from "@uncaged/workflow-runtime";
const table: ModeratorTable<MyMeta> = {
[START]: [{ condition: "FALLBACK", role: "firstRole" }],
firstRole: [{ condition: "FALLBACK", role: END }],
};
\`\`\`
## AdapterFn / AdapterBinding
The adapter receives a system prompt and Zod schema, returns a \`RoleFn<T>\` that produces typed meta:
\`\`\`typescript
type AdapterFn = <T>(prompt: string, schema: ZodType<T>) => RoleFn<T>;
type AdapterBinding = {
adapter: AdapterFn;
overrides: Partial<Record<string, AdapterFn>> | null;
};
\`\`\`
The **ModeratorTable** on **WorkflowDefinition** is declarative routing (from each role and \`START\` to the next role or \`END\`); the engine evaluates conditions at runtime.
## Role Definition
@@ -258,16 +230,15 @@ Each role has:
# 1. Initialize a workspace
uncaged-workflow init workspace my-workflow
# 2. Write your template (roles + ModeratorTable + definition)
# 3. Write entry file (workflows/*-entry.ts) with adapter binding + descriptor
# 2. Write your template (roles + ModeratorTable + descriptor)
# 4. Build the ESM bundle
bun run bundle # uses scripts/bundle.ts
# 3. Build the ESM bundle
bun run build
# 5. Register locally
uncaged-workflow workflow add my-workflow ./dist/my-workflow-entry.esm.js
# 4. Register locally
uncaged-workflow workflow add my-workflow ./dist/my-workflow.esm.js
# 6. Test
# 5. Test
uncaged-workflow run my-workflow --prompt "test task"
uncaged-workflow live --latest
\`\`\`
@@ -275,46 +246,5 @@ uncaged-workflow live --latest
## Versioning
Bundles are immutable and identified by XXH64 hash. Re-registering a workflow with a new bundle creates a new version. Use \`workflow history\` and \`workflow rollback\` to manage versions.
## Pitfalls
### Lazy initialization is mandatory
The bundle is \`import()\`-ed at register time (\`workflow add\`) to read the descriptor. At that point, no runtime env vars (API keys, etc.) are available.
**Never read env at module top-level.** Wrap provider/adapter creation in a lazy closure:
\`\`\`typescript
// ❌ WRONG — breaks register
const provider = { apiKey: process.env.MY_KEY! };
const adapter = createAdapter(provider);
// ✅ CORRECT — only reads env when run() is called
function createLazyAdapter(): AdapterFn {
let cached: Provider | null = null;
return (prompt, schema) => {
return async (ctx, runtime) => {
if (!cached) cached = { apiKey: process.env.MY_KEY! };
// ... use cached provider
};
};
}
\`\`\`
### Bundle import restrictions
The bundle validator only allows these import specifiers:
- Node built-ins (\`node:fs\`, \`node:path\`, etc.)
- \`@uncaged/workflow-*\` packages
Third-party packages (**including zod**) must be bundled into the \`.esm.js\` file, not left as external imports. When using \`bun build\`, only mark \`@uncaged/*\` as external.
### No default exports
The engine only reads named exports \`run\` and \`descriptor\`. Using \`export default\` will cause registration to fail silently.
### Single-file ESM
The bundle must be a single \`.esm.js\` file. No dynamic \`import()\` inside the bundle — it breaks hash verification and the loader sandbox.
`;
}
+10
View File
@@ -1,6 +1,11 @@
#!/usr/bin/env bash
# Publish all public @uncaged/* packages to Gitea npm registry.
#
# PITFALL: After bumping versions in package.json, bun pm pack still reads the
# old bun.lock and resolves workspace:* to the previous (stale) versions.
# This script deletes bun.lock and runs bun install before packing to force
# correct resolution of workspace:* dependencies.
#
# Usage:
# ./scripts/publish-all.sh # Publish all packages
# ./scripts/publish-all.sh --dry-run # Show what would be published
@@ -95,6 +100,11 @@ for name in result:
print(name_to_dir[name])
")
# Regenerate lockfile so bun pm pack resolves workspace:* to freshly-bumped versions
cd "$MONOREPO_ROOT"
rm -f bun.lock
bun install
ok=0
fail=0