Merge pull request 'refactor: remove all remaining uncaged codename references' (#24) from refactor/remove-uncaged-codename into main
CI / check (push) Failing after 7m35s

This commit was merged in pull request #24.
This commit is contained in:
2026-06-02 16:17:11 +00:00
51 changed files with 214 additions and 271 deletions
+9 -9
View File
@@ -17,7 +17,7 @@ roles:
docker run -d --name uwf-e2e-$$ \ docker run -d --name uwf-e2e-$$ \
-v "$(pwd):/workspace:ro" \ -v "$(pwd):/workspace:ro" \
-e HOME=/root \ -e HOME=/root \
-e UNCAGED_WORKFLOW_STORAGE_ROOT=/tmp/uwf-e2e-storage \ -e UWF_STORAGE_ROOT=/tmp/uwf-e2e-storage \
--add-host=host.docker.internal:host-gateway \ --add-host=host.docker.internal:host-gateway \
-w /workspace \ -w /workspace \
node:22-bookworm \ node:22-bookworm \
@@ -39,7 +39,7 @@ roles:
export PATH="$HOME/.bun/bin:$PATH" export PATH="$HOME/.bun/bin:$PATH"
# Isolated storage # Isolated storage
mkdir -p $UNCAGED_WORKFLOW_STORAGE_ROOT mkdir -p $UWF_STORAGE_ROOT
# Install workspace deps # Install workspace deps
cd /root/workflow && bun install cd /root/workflow && bun install
@@ -60,11 +60,11 @@ roles:
The host config contains provider credentials and model settings needed for LLM calls. The host config contains provider credentials and model settings needed for LLM calls.
Also rewrite any localhost URLs to host.docker.internal so the container can reach host services. Also rewrite any localhost URLs to host.docker.internal so the container can reach host services.
``` ```
docker cp ~/.uncaged/workflow/config.yaml uwf-e2e-$$:/tmp/uwf-e2e-storage/config.yaml 2>/dev/null || true docker cp ~/.uwf/config.yaml uwf-e2e-$$:/tmp/uwf-e2e-storage/config.yaml 2>/dev/null || true
docker exec uwf-e2e-$$ bash -c ' docker exec uwf-e2e-$$ bash -c '
if [ -f $UNCAGED_WORKFLOW_STORAGE_ROOT/config.yaml ]; then if [ -f $UWF_STORAGE_ROOT/config.yaml ]; then
sed -i "s|localhost|host.docker.internal|g; s|127\.0\.0\.1|host.docker.internal|g" \ sed -i "s|localhost|host.docker.internal|g; s|127\.0\.0\.1|host.docker.internal|g" \
$UNCAGED_WORKFLOW_STORAGE_ROOT/config.yaml $UWF_STORAGE_ROOT/config.yaml
fi fi
' '
``` ```
@@ -95,7 +95,7 @@ roles:
All commands use `uwf` (installed via `bun link` inside the container). All commands use `uwf` (installed via `bun link` inside the container).
Remember to set env vars in each exec: Remember to set env vars in each exec:
export PATH="$HOME/.bun/bin:$PATH" export PATH="$HOME/.bun/bin:$PATH"
export UNCAGED_WORKFLOW_STORAGE_ROOT=/tmp/uwf-e2e-storage export UWF_STORAGE_ROOT=/tmp/uwf-e2e-storage
Config tests: Config tests:
1. `uwf config list` — verify it returns valid JSON 1. `uwf config list` — verify it returns valid JSON
@@ -133,7 +133,7 @@ roles:
procedure: | procedure: |
Use the container (containerName) and workflow (workflowName) from your prompt. Use the container (containerName) and workflow (workflowName) from your prompt.
All commands via: `docker exec <containerName> bash -c '...'` All commands via: `docker exec <containerName> bash -c '...'`
Set env: PATH="$HOME/.bun/bin:$PATH" UNCAGED_WORKFLOW_STORAGE_ROOT=/tmp/uwf-e2e-storage Set env: PATH="$HOME/.bun/bin:$PATH" UWF_STORAGE_ROOT=/tmp/uwf-e2e-storage
1. `uwf thread start <workflowName> -p 'E2E test: what is 2+2?'` — capture thread ID from JSON output 1. `uwf thread start <workflowName> -p 'E2E test: what is 2+2?'` — capture thread ID from JSON output
2. `uwf thread list` — verify the thread appears in the list 2. `uwf thread list` — verify the thread appears in the list
@@ -166,7 +166,7 @@ roles:
procedure: | procedure: |
Use the container (containerName) and threadId from your prompt. Use the container (containerName) and threadId from your prompt.
All commands via: `docker exec <containerName> bash -c '...'` All commands via: `docker exec <containerName> bash -c '...'`
Set env: PATH="$HOME/.bun/bin:$PATH" UNCAGED_WORKFLOW_STORAGE_ROOT=/tmp/uwf-e2e-storage Set env: PATH="$HOME/.bun/bin:$PATH" UWF_STORAGE_ROOT=/tmp/uwf-e2e-storage
Step inspection: Step inspection:
1. `uwf step list <threadId>` — verify steps array has length > 1 1. `uwf step list <threadId>` — verify steps array has length > 1
@@ -208,7 +208,7 @@ roles:
procedure: | procedure: |
Use containerName, threadId, lastStepHash, and workflowName from your prompt. Use containerName, threadId, lastStepHash, and workflowName from your prompt.
All commands via: `docker exec <containerName> bash -c '...'` All commands via: `docker exec <containerName> bash -c '...'`
Set env: PATH="$HOME/.bun/bin:$PATH" UNCAGED_WORKFLOW_STORAGE_ROOT=/tmp/uwf-e2e-storage Set env: PATH="$HOME/.bun/bin:$PATH" UWF_STORAGE_ROOT=/tmp/uwf-e2e-storage
Cancel: Cancel:
1. Start a second thread: `uwf thread start <workflowName> -p 'E2E cancel test'` 1. Start a second thread: `uwf thread start <workflowName> -p 'E2E cancel test'`
+1 -1
View File
@@ -201,7 +201,7 @@ roles:
"https://git.shazhou.work/api/v1/repos/<owner>/<repo>/pulls" \ "https://git.shazhou.work/api/v1/repos/<owner>/<repo>/pulls" \
-d '{"title":"...","body":"...","head":"<branch>","base":"main"}' -d '{"title":"...","body":"...","head":"<branch>","base":"main"}'
``` ```
- The repo remote (owner/repo format, e.g. "uncaged/workflow") is given in your task prompt — use it directly. - The repo remote (owner/repo format, e.g. "shazhou/united-workforce") is given in your task prompt — use it directly.
- PR body must include: What / Why / Changes / Ref sections, with `Fixes #N` in Ref - PR body must include: What / Why / Changes / Ref sections, with `Fixes #N` in Ref
6. **Verify PR was created** — parse the curl response JSON: it must contain a `"number"` field. Print the PR URL. 6. **Verify PR was created** — parse the curl response JSON: it must contain a `"number"` field. Print the PR URL.
- If curl returns an error or no number field: capture the response, mark hook_failed - If curl returns an error or no number field: capture the response, mark hook_failed
+12 -12
View File
@@ -1,6 +1,6 @@
# UWF Bootstrap Guide # UWF Bootstrap Guide
This guide helps any AI agent set up `uwf` (Uncaged Workflow) from scratch — or self-check and upgrade an existing installation. This guide helps any AI agent set up `uwf` (United Workforce) from scratch — or self-check and upgrade an existing installation.
## Prerequisites ## Prerequisites
@@ -16,7 +16,7 @@ This guide helps any AI agent set up `uwf` (Uncaged Workflow) from scratch — o
### 1. Install uwf CLI ### 1. Install uwf CLI
```bash ```bash
bun install -g @uncaged/cli bun install -g @united-workforce/cli
``` ```
**Check:** `uwf --version` prints a version number (e.g. `0.5.1`). **Check:** `uwf --version` prints a version number (e.g. `0.5.1`).
@@ -27,11 +27,11 @@ Install the adapter that matches your agent runtime. Pick **one**:
| Agent | Package | Binary | | Agent | Package | Binary |
|-------|---------|--------| |-------|---------|--------|
| Hermes | `@uncaged/agent-hermes` | `uwf-hermes` | | Hermes | `@united-workforce/agent-hermes` | `uwf-hermes` |
```bash ```bash
# Example: Hermes agent # Example: Hermes agent
bun install -g @uncaged/agent-hermes bun install -g @united-workforce/agent-hermes
``` ```
**Check:** `uwf-hermes --version` prints a version number. **Check:** `uwf-hermes --version` prints a version number.
@@ -55,7 +55,7 @@ uwf setup \
--agent hermes --agent hermes
``` ```
This creates `~/.uncaged/workflow/config.yaml` with your provider, model, and default agent. This creates `~/.uwf/config.yaml` with your provider, model, and default agent.
#### Config Structure #### Config Structure
@@ -76,7 +76,7 @@ defaultAgent: hermes
defaultModel: default defaultModel: default
``` ```
**Check:** `cat ~/.uncaged/workflow/config.yaml` shows valid provider, model, and agent config. **Check:** `cat ~/.uwf/config.yaml` shows valid provider, model, and agent config.
### 4. Verify Installation ### 4. Verify Installation
@@ -129,23 +129,23 @@ Compare with latest published versions:
```bash ```bash
bun pm ls -g | grep -E "cli|workflow-agent" bun pm ls -g | grep -E "cli|workflow-agent"
npm info @uncaged/cli version npm info @united-workforce/cli version
npm info @uncaged/agent-hermes version npm info @united-workforce/agent-hermes version
``` ```
If local version < published version, upgrade: If local version < published version, upgrade:
```bash ```bash
bun install -g @uncaged/cli@latest bun install -g @united-workforce/cli@latest
bun install -g @uncaged/agent-hermes@latest bun install -g @united-workforce/agent-hermes@latest
``` ```
**Check:** `uwf --version` matches `npm info @uncaged/cli version`. **Check:** `uwf --version` matches `npm info @united-workforce/cli version`.
### Config Check ### Config Check
```bash ```bash
cat ~/.uncaged/workflow/config.yaml cat ~/.uwf/config.yaml
``` ```
Verify: Verify:
+4 -4
View File
@@ -1,4 +1,4 @@
# Contributing to @uncaged/workflow # Contributing to @united-workforce/cli
Thank you for your interest in contributing! This guide covers setup, conventions, and the PR workflow. Thank you for your interest in contributing! This guide covers setup, conventions, and the PR workflow.
@@ -11,8 +11,8 @@ Thank you for your interest in contributing! This guide covers setup, convention
## Setup ## Setup
```bash ```bash
git clone https://github.com/shazhou-ww/uncaged-workflow.git git clone https://github.com/shazhou-ww/united-workforce.git
cd uncaged-workflow cd united-workforce
bun install bun install
bun run build bun run build
bun test bun test
@@ -36,7 +36,7 @@ See [CLAUDE.md](CLAUDE.md) for the full coding standard. Key points:
- **Functional-first** — `function` + `type`, not `class` + `interface` - **Functional-first** — `function` + `type`, not `class` + `interface`
- **No optional properties** — use `T | null` instead of `?:` - **No optional properties** — use `T | null` instead of `?:`
- **Named exports only** — no default exports - **Named exports only** — no default exports
- **No `console.log`** — use the structured logger from `@uncaged/util` - **No `console.log`** — use the structured logger from `@united-workforce/util`
- **Static imports only** — no `await import()` in production code - **Static imports only** — no `await import()` in production code
- **Biome** for lint + format — run `bun run check` before committing - **Biome** for lint + format — run `bun run check` before committing
+1 -1
View File
@@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2026 Uncaged Copyright (c) 2026 United Workforce
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
+1 -1
View File
@@ -1,6 +1,6 @@
# United Workforce (uwf) # United Workforce (uwf)
[![CI](https://github.com/shazhou-ww/uncaged-workflow/actions/workflows/ci.yml/badge.svg)](https://github.com/shazhou-ww/uncaged-workflow/actions/workflows/ci.yml) [![CI](https://github.com/shazhou-ww/united-workforce/actions/workflows/ci.yml/badge.svg)](https://github.com/shazhou-ww/united-workforce/actions/workflows/ci.yml)
[![npm](https://img.shields.io/npm/v/@united-workforce/cli?label=%40united-workforce%2Fcli)](https://www.npmjs.com/package/@united-workforce/cli) [![npm](https://img.shields.io/npm/v/@united-workforce/cli?label=%40united-workforce%2Fcli)](https://www.npmjs.com/package/@united-workforce/cli)
[![npm](https://img.shields.io/npm/v/@united-workforce/protocol?label=%40united-workforce%2Fprotocol)](https://www.npmjs.com/package/@united-workforce/protocol) [![npm](https://img.shields.io/npm/v/@united-workforce/protocol?label=%40united-workforce%2Fprotocol)](https://www.npmjs.com/package/@united-workforce/protocol)
[![npm](https://img.shields.io/npm/v/@united-workforce/util-agent?label=%40united-workforce%2Futil-agent)](https://www.npmjs.com/package/@united-workforce/util-agent) [![npm](https://img.shields.io/npm/v/@united-workforce/util-agent?label=%40united-workforce%2Futil-agent)](https://www.npmjs.com/package/@united-workforce/util-agent)
+1 -1
View File
@@ -358,7 +358,7 @@ threads.yaml: { "01J7K9...4T": "8FWKR3TN5V1QA" }
## Storage layout ## Storage layout
``` ```
~/.uncaged/workflow/ ~/.uwf/
├── cas/ # json-cas filesystem store (all CAS nodes) ├── cas/ # json-cas filesystem store (all CAS nodes)
├── config.yaml # Provider, model, agent configuration ├── config.yaml # Provider, model, agent configuration
├── threads.yaml # Active thread head pointers: threadId → CasRef ├── threads.yaml # Active thread head pointers: threadId → CasRef
+2 -2
View File
@@ -82,7 +82,7 @@ Agent 解析优先级(`resolveAgentConfig`):
```33:43:packages/util-agent/src/storage.ts ```33:43:packages/util-agent/src/storage.ts
export function resolveStorageRoot(): string { export function resolveStorageRoot(): string {
const internal = process.env.UNCAGED_WORKFLOW_STORAGE_ROOT; const internal = process.env.UWF_STORAGE_ROOT;
if (internal !== undefined && internal !== "") { if (internal !== undefined && internal !== "") {
return internal; return internal;
} }
@@ -94,7 +94,7 @@ export function resolveStorageRoot(): string {
} }
``` ```
Agent 子进程通过继承的 `process.env` 与父 CLI 共享同一 storage root;`createAgent` 内还会 `loadDotenv({ path: getEnvPath(storageRoot) })` 加载 `~/.uncaged/workflow/.env`。 Agent 子进程通过继承的 `process.env` 与父 CLI 共享同一 storage root;`createAgent` 内还会 `loadDotenv({ path: getEnvPath(storageRoot) })` 加载 `~/.uwf/.env`。
#### Agent 侧职责(设计文档 + 实现) #### Agent 侧职责(设计文档 + 实现)
+6 -6
View File
@@ -202,7 +202,7 @@ payload:
- `graph``Record<Role | "$START", Record<Status, Target>>`,每个 Target = `{ role, prompt }` - `graph``Record<Role | "$START", Record<Status, Target>>`,每个 Target = `{ role, prompt }`
- Status 来自上一个 role 输出的 `status` 字段,`$START``_` 作为初始 status - Status 来自上一个 role 输出的 `status` 字段,`$START``_` 作为初始 status
- Prompt 模板使用 Mustache 渲染,变量来自 lastOutput - Prompt 模板使用 Mustache 渲染,变量来自 lastOutput
- 不含 agent binding — agent 配置在 `~/.uncaged/workflow/config.yaml` 中管理 - 不含 agent binding — agent 配置在 `~/.uwf/config.yaml` 中管理
Moderator 的求值逻辑: Moderator 的求值逻辑:
@@ -276,7 +276,7 @@ threads.yaml: { "01J7K9M2XNPQR5VWBCDF8G3H4T": "8FWKR3TN5V1QA" }
系统两个顶层 YAML 文件和一个 env 文件: 系统两个顶层 YAML 文件和一个 env 文件:
```yaml ```yaml
# ~/.uncaged/workflow/config.yaml — 全局配置 # ~/.uwf/config.yaml — 全局配置
providers: providers:
openai: openai:
baseUrl: "https://api.openai.com/v1" baseUrl: "https://api.openai.com/v1"
@@ -315,7 +315,7 @@ modelOverrides:
``` ```
```yaml ```yaml
# ~/.uncaged/workflow/threads.yaml — active thread 链头指针 # ~/.uwf/threads.yaml — active thread 链头指针
01J7K9M2XNPQR5VWBCDF8G3H4T: "8FWKR3TN5V1QA" 01J7K9M2XNPQR5VWBCDF8G3H4T: "8FWKR3TN5V1QA"
01J8AB3QRMSTV6WKXZ2C4DF7GN: "3CNWT9KR6D2HV" 01J8AB3QRMSTV6WKXZ2C4DF7GN: "3CNWT9KR6D2HV"
``` ```
@@ -323,7 +323,7 @@ modelOverrides:
Thread 结束时从 threads.yaml 移除。可选:追加到 `history.jsonl` 做归档。 Thread 结束时从 threads.yaml 移除。可选:追加到 `history.jsonl` 做归档。
```bash ```bash
# ~/.uncaged/workflow/.env — 敏感信息(API keys) # ~/.uwf/.env — 敏感信息(API keys)
OPENAI_API_KEY=sk-... OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-... ANTHROPIC_API_KEY=sk-ant-...
OPENROUTER_API_KEY=sk-or-... OPENROUTER_API_KEY=sk-or-...
@@ -478,7 +478,7 @@ type AgentConfig = {
args: string[]; args: string[];
}; };
/** ~/.uncaged/workflow/config.yaml */ /** ~/.uwf/config.yaml */
type WorkflowConfig = { type WorkflowConfig = {
providers: Record<ProviderAlias, ProviderConfig>; providers: Record<ProviderAlias, ProviderConfig>;
models: Record<ModelAlias, ModelConfig>; models: Record<ModelAlias, ModelConfig>;
@@ -489,7 +489,7 @@ type WorkflowConfig = {
modelOverrides: Record<Scenario, ModelAlias> | null; modelOverrides: Record<Scenario, ModelAlias> | null;
}; };
/** ~/.uncaged/workflow/threads.yaml */ /** ~/.uwf/threads.yaml */
type ThreadsIndex = Record<ThreadId, CasRef>; type ThreadsIndex = Record<ThreadId, CasRef>;
// ^ thread-id ^ head StepNode/StartNode hash // ^ thread-id ^ head StepNode/StartNode hash
``` ```
+3 -3
View File
@@ -32,11 +32,11 @@
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/shazhou-ww/uncaged-workflow.git" "url": "https://github.com/shazhou-ww/united-workforce.git"
}, },
"homepage": "https://github.com/shazhou-ww/uncaged-workflow#readme", "homepage": "https://github.com/shazhou-ww/united-workforce#readme",
"bugs": { "bugs": {
"url": "https://github.com/shazhou-ww/uncaged-workflow/issues" "url": "https://github.com/shazhou-ww/united-workforce/issues"
}, },
"license": "MIT" "license": "MIT"
} }
+1 -1
View File
@@ -136,6 +136,6 @@ src/
## Configuration ## Configuration
Requires a configured OpenAI-compatible provider and model in `~/.uncaged/workflow/config.yaml` (via `uwf setup`). API keys are loaded from `~/.uncaged/workflow/.env`. Requires a configured OpenAI-compatible provider and model in `~/.uwf/config.yaml` (via `uwf setup`). API keys are loaded from `~/.uwf/.env`.
Tools run with the current working directory as `ToolContext.cwd` (typically the directory where `uwf thread step` was invoked). Tools run with the current working directory as `ToolContext.cwd` (typically the directory where `uwf thread step` was invoked).
+3 -3
View File
@@ -35,12 +35,12 @@
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://git.shazhou.work/uncaged/workflow.git", "url": "https://git.shazhou.work/shazhou/united-workforce.git",
"directory": "packages/agent-builtin" "directory": "packages/agent-builtin"
}, },
"homepage": "https://git.shazhou.work/uncaged/workflow#readme", "homepage": "https://git.shazhou.work/shazhou/united-workforce#readme",
"bugs": { "bugs": {
"url": "https://git.shazhou.work/uncaged/workflow/issues" "url": "https://git.shazhou.work/shazhou/united-workforce/issues"
}, },
"license": "MIT" "license": "MIT"
} }
+3 -3
View File
@@ -35,12 +35,12 @@
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://git.shazhou.work/uncaged/workflow.git", "url": "https://git.shazhou.work/shazhou/united-workforce.git",
"directory": "packages/agent-claude-code" "directory": "packages/agent-claude-code"
}, },
"homepage": "https://git.shazhou.work/uncaged/workflow#readme", "homepage": "https://git.shazhou.work/shazhou/united-workforce#readme",
"bugs": { "bugs": {
"url": "https://git.shazhou.work/uncaged/workflow/issues" "url": "https://git.shazhou.work/shazhou/united-workforce/issues"
}, },
"license": "MIT" "license": "MIT"
} }
+1 -1
View File
@@ -96,6 +96,6 @@ src/
## Configuration ## Configuration
Uses workflow config from `~/.uncaged/workflow/config.yaml` (via agent-kit). Hermes session files are stored under the workflow storage root (see `session-detail.ts`). Uses workflow config from `~/.uwf/config.yaml` (via agent-kit). Hermes session files are stored under the workflow storage root (see `session-detail.ts`).
Set `UWF_HERMES_NO_RESUME=1` to disable session resume (see `isResumeDisabled` in `session-cache.ts`). Set `UWF_HERMES_NO_RESUME=1` to disable session resume (see `isResumeDisabled` in `session-cache.ts`).
+3 -3
View File
@@ -36,12 +36,12 @@
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://git.shazhou.work/uncaged/workflow.git", "url": "https://git.shazhou.work/shazhou/united-workforce.git",
"directory": "packages/agent-hermes" "directory": "packages/agent-hermes"
}, },
"homepage": "https://git.shazhou.work/uncaged/workflow#readme", "homepage": "https://git.shazhou.work/shazhou/united-workforce#readme",
"bugs": { "bugs": {
"url": "https://git.shazhou.work/uncaged/workflow/issues" "url": "https://git.shazhou.work/shazhou/united-workforce/issues"
}, },
"engines": { "engines": {
"bun": ">= 1.0.0" "bun": ">= 1.0.0"
+9 -9
View File
@@ -120,7 +120,7 @@ uwf setup --provider openai --base-url https://api.openai.com/v1 \
--api-key sk-... --model gpt-4o --agent hermes --api-key sk-... --model gpt-4o --agent hermes
``` ```
Config: `~/.uncaged/workflow/config.yaml` (includes API keys). Config: `~/.uwf/config.yaml` (includes API keys).
### Skill ### Skill
@@ -206,17 +206,17 @@ src/
| File | Purpose | | File | Purpose |
|------|---------| |------|---------|
| `~/.uncaged/workflow/config.yaml` | Providers, models, default agent | | `~/.uwf/config.yaml` | Providers, models, default agent |
| `~/.uncaged/workflow/.env` | API keys (referenced by `apiKeyEnv` in config) | | `~/.uwf/.env` | API keys (referenced by `apiKeyEnv` in config) |
| `~/.uncaged/workflow/registry.yaml` | Workflow name → CAS hash | | `~/.uwf/registry.yaml` | Workflow name → CAS hash |
| `~/.uncaged/workflow/threads.yaml` | Active thread head pointers | | `~/.uwf/threads.yaml` | Active thread head pointers |
| `~/.uncaged/json-cas/` | Content-addressed node storage (unified CAS store, shared with `ocas` CLI) | | `~/.ocas/` | Content-addressed node storage (unified CAS store, shared with `ocas` CLI) |
### Environment Variables ### Environment Variables
| Variable | Purpose | Default | | Variable | Purpose | Default |
|----------|---------|---------| |----------|---------|---------|
| `UNCAGED_CAS_DIR` | Override the global CAS directory location | `~/.uncaged/json-cas` | | `OCAS_DIR` | Override the global CAS directory location | `~/.ocas` |
| `UNCAGED_WORKFLOW_STORAGE_ROOT` | Internal override for workflow metadata storage | `~/.uncaged/workflow` | | `UWF_STORAGE_ROOT` | Internal override for workflow metadata storage | `~/.uwf` |
| `WORKFLOW_STORAGE_ROOT` | User override for workflow metadata storage | `~/.uncaged/workflow` | | `WORKFLOW_STORAGE_ROOT` | User override for workflow metadata storage | `~/.uwf` |
+3 -3
View File
@@ -34,12 +34,12 @@
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://git.shazhou.work/uncaged/workflow.git", "url": "https://git.shazhou.work/shazhou/united-workforce.git",
"directory": "packages/cli" "directory": "packages/cli"
}, },
"homepage": "https://git.shazhou.work/uncaged/workflow#readme", "homepage": "https://git.shazhou.work/shazhou/united-workforce#readme",
"bugs": { "bugs": {
"url": "https://git.shazhou.work/uncaged/workflow/issues" "url": "https://git.shazhou.work/shazhou/united-workforce/issues"
}, },
"license": "MIT" "license": "MIT"
} }
@@ -67,7 +67,7 @@ describe("C1: adapter JSON round-trip integration", () => {
prompt: "Test round-trip task", prompt: "Test round-trip task",
}); });
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
const threadId = "01ROUNDTRIPTEST0000000000" as ThreadId; const threadId = "01ROUNDTRIPTEST0000000000" as ThreadId;
await seedThreads(tmpDir, { [threadId]: startHash }); await seedThreads(tmpDir, { [threadId]: startHash });
@@ -134,7 +134,7 @@ describe("C1: adapter JSON round-trip integration", () => {
env: { env: {
...process.env, ...process.env,
WORKFLOW_STORAGE_ROOT: tmpDir, WORKFLOW_STORAGE_ROOT: tmpDir,
UNCAGED_CAS_DIR: casDir, OCAS_DIR: casDir,
}, },
cwd: tmpDir, cwd: tmpDir,
timeout: 30000, timeout: 30000,
@@ -225,9 +225,9 @@ describe("currentRole field", () => {
await mkdir(storageRoot, { recursive: true }); await mkdir(storageRoot, { recursive: true });
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
// Set UNCAGED_CAS_DIR for this test // Set OCAS_DIR for this test
originalEnv = process.env.UNCAGED_CAS_DIR; originalEnv = process.env.OCAS_DIR;
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
} }
async function teardown() { async function teardown() {
@@ -236,9 +236,9 @@ describe("currentRole field", () => {
} }
// Restore original environment // Restore original environment
if (originalEnv === undefined) { if (originalEnv === undefined) {
delete process.env.UNCAGED_CAS_DIR; delete process.env.OCAS_DIR;
} else { } else {
process.env.UNCAGED_CAS_DIR = originalEnv; process.env.OCAS_DIR = originalEnv;
} }
} }
@@ -12,7 +12,7 @@ beforeEach(async () => {
tmpDir = await mkdtemp(join(tmpdir(), "cli-uwf-resolve-head-")); tmpDir = await mkdtemp(join(tmpdir(), "cli-uwf-resolve-head-"));
const casDir = join(tmpDir, "cas"); const casDir = join(tmpDir, "cas");
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
}); });
afterEach(async () => { afterEach(async () => {
+18 -18
View File
@@ -70,16 +70,16 @@ let originalEnv: string | undefined;
beforeEach(async () => { beforeEach(async () => {
tmpDir = await mkdtemp(join(tmpdir(), "cli-uwf-step-read-test-")); tmpDir = await mkdtemp(join(tmpdir(), "cli-uwf-step-read-test-"));
originalEnv = process.env.UNCAGED_CAS_DIR; originalEnv = process.env.OCAS_DIR;
}); });
afterEach(async () => { afterEach(async () => {
await rm(tmpDir, { recursive: true, force: true }); await rm(tmpDir, { recursive: true, force: true });
// Restore original environment // Restore original environment
if (originalEnv === undefined) { if (originalEnv === undefined) {
delete process.env.UNCAGED_CAS_DIR; delete process.env.OCAS_DIR;
} else { } else {
process.env.UNCAGED_CAS_DIR = originalEnv; process.env.OCAS_DIR = originalEnv;
} }
}); });
@@ -88,10 +88,10 @@ afterEach(async () => {
describe("step read", () => { describe("step read", () => {
test("test 1: basic single-step read with 3 turns", async () => { test("test 1: basic single-step read with 3 turns", async () => {
const casDir = join(tmpDir, "cas"); const casDir = join(tmpDir, "cas");
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
const store = createFsStore(casDir); const store = createFsStore(casDir);
const schemas = await registerUwfSchemas(store); const schemas = await registerUwfSchemas(store);
const detailSchemas = await registerDetailSchemas(store); const detailSchemas = await registerDetailSchemas(store);
@@ -177,9 +177,9 @@ describe("step read", () => {
test("test 2: quota enforcement - multiple turns", async () => { test("test 2: quota enforcement - multiple turns", async () => {
const casDir = join(tmpDir, "cas"); const casDir = join(tmpDir, "cas");
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
const store = createFsStore(casDir); const store = createFsStore(casDir);
const schemas = await registerUwfSchemas(store); const schemas = await registerUwfSchemas(store);
const detailSchemas = await registerDetailSchemas(store); const detailSchemas = await registerDetailSchemas(store);
@@ -263,9 +263,9 @@ describe("step read", () => {
test("test 3: minimal quota edge case - always show at least one turn", async () => { test("test 3: minimal quota edge case - always show at least one turn", async () => {
const casDir = join(tmpDir, "cas"); const casDir = join(tmpDir, "cas");
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
const store = createFsStore(casDir); const store = createFsStore(casDir);
const schemas = await registerUwfSchemas(store); const schemas = await registerUwfSchemas(store);
const detailSchemas = await registerDetailSchemas(store); const detailSchemas = await registerDetailSchemas(store);
@@ -340,9 +340,9 @@ describe("step read", () => {
test("test 4: step with no detail field", async () => { test("test 4: step with no detail field", async () => {
const casDir = join(tmpDir, "cas"); const casDir = join(tmpDir, "cas");
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
const store = createFsStore(casDir); const store = createFsStore(casDir);
const schemas = await registerUwfSchemas(store); const schemas = await registerUwfSchemas(store);
@@ -401,9 +401,9 @@ describe("step read", () => {
test("test 5: step with detail but no turns array", async () => { test("test 5: step with detail but no turns array", async () => {
const casDir = join(tmpDir, "cas"); const casDir = join(tmpDir, "cas");
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
const store = createFsStore(casDir); const store = createFsStore(casDir);
const schemas = await registerUwfSchemas(store); const schemas = await registerUwfSchemas(store);
await registerDetailSchemas(store); await registerDetailSchemas(store);
@@ -479,9 +479,9 @@ describe("step read", () => {
test("test 6: displays role and tool calls in turn body", async () => { test("test 6: displays role and tool calls in turn body", async () => {
const casDir = join(tmpDir, "cas"); const casDir = join(tmpDir, "cas");
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
const store = createFsStore(casDir); const store = createFsStore(casDir);
const schemas = await registerUwfSchemas(store); const schemas = await registerUwfSchemas(store);
const detailSchemas = await registerDetailSchemas(store); const detailSchemas = await registerDetailSchemas(store);
@@ -553,9 +553,9 @@ describe("step read", () => {
test("test 7: turn content with special characters", async () => { test("test 7: turn content with special characters", async () => {
const casDir = join(tmpDir, "cas"); const casDir = join(tmpDir, "cas");
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
const store = createFsStore(casDir); const store = createFsStore(casDir);
const schemas = await registerUwfSchemas(store); const schemas = await registerUwfSchemas(store);
const detailSchemas = await registerDetailSchemas(store); const detailSchemas = await registerDetailSchemas(store);
@@ -131,16 +131,16 @@ describe("cmdStepShow JSON serialization", () => {
testDir = await mkdtemp(join(tmpdir(), "uwf-test-")); testDir = await mkdtemp(join(tmpdir(), "uwf-test-"));
casDir = join(testDir, "cas"); casDir = join(testDir, "cas");
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
originalEnv = process.env.UNCAGED_CAS_DIR; originalEnv = process.env.OCAS_DIR;
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
}); });
afterEach(async () => { afterEach(async () => {
await rm(testDir, { recursive: true, force: true }); await rm(testDir, { recursive: true, force: true });
if (originalEnv === undefined) { if (originalEnv === undefined) {
delete process.env.UNCAGED_CAS_DIR; delete process.env.OCAS_DIR;
} else { } else {
process.env.UNCAGED_CAS_DIR = originalEnv; process.env.OCAS_DIR = originalEnv;
} }
}); });
@@ -67,17 +67,17 @@ let originalEnv: string | undefined;
beforeEach(async () => { beforeEach(async () => {
tmpDir = await mkdtemp(join(tmpdir(), "cli-uwf-step-timing-test-")); tmpDir = await mkdtemp(join(tmpdir(), "cli-uwf-step-timing-test-"));
originalEnv = process.env.UNCAGED_CAS_DIR; originalEnv = process.env.OCAS_DIR;
process.env.UNCAGED_CAS_DIR = join(tmpDir, "cas"); process.env.OCAS_DIR = join(tmpDir, "cas");
await mkdir(process.env.UNCAGED_CAS_DIR, { recursive: true }); await mkdir(process.env.OCAS_DIR, { recursive: true });
}); });
afterEach(async () => { afterEach(async () => {
await rm(tmpDir, { recursive: true, force: true }); await rm(tmpDir, { recursive: true, force: true });
if (originalEnv === undefined) { if (originalEnv === undefined) {
delete process.env.UNCAGED_CAS_DIR; delete process.env.OCAS_DIR;
} else { } else {
process.env.UNCAGED_CAS_DIR = originalEnv; process.env.OCAS_DIR = originalEnv;
} }
}); });
@@ -16,13 +16,11 @@ import {
describe("Global CAS directory", () => { describe("Global CAS directory", () => {
let tmpDir: string; let tmpDir: string;
let originalOcasDir: string | undefined; let originalOcasDir: string | undefined;
let originalLegacyCasDir: string | undefined;
beforeEach(async () => { beforeEach(async () => {
tmpDir = join(tmpdir(), `uwf-test-global-cas-${Date.now()}`); tmpDir = join(tmpdir(), `uwf-test-global-cas-${Date.now()}`);
await mkdir(tmpDir, { recursive: true }); await mkdir(tmpDir, { recursive: true });
originalOcasDir = process.env.OCAS_DIR; originalOcasDir = process.env.OCAS_DIR;
originalLegacyCasDir = process.env.UNCAGED_CAS_DIR;
}); });
afterEach(async () => { afterEach(async () => {
@@ -34,16 +32,10 @@ describe("Global CAS directory", () => {
} else { } else {
process.env.OCAS_DIR = originalOcasDir; process.env.OCAS_DIR = originalOcasDir;
} }
if (originalLegacyCasDir === undefined) {
delete process.env.UNCAGED_CAS_DIR;
} else {
process.env.UNCAGED_CAS_DIR = originalLegacyCasDir;
}
}); });
test("getGlobalCasDir returns default path when no env var set", () => { test("getGlobalCasDir returns default path when no env var set", () => {
delete process.env.OCAS_DIR; delete process.env.OCAS_DIR;
delete process.env.UNCAGED_CAS_DIR;
const casDir = getGlobalCasDir(); const casDir = getGlobalCasDir();
expect(casDir).toContain(".ocas"); expect(casDir).toContain(".ocas");
}); });
@@ -55,22 +47,8 @@ describe("Global CAS directory", () => {
expect(casDir).toBe(customPath); expect(casDir).toBe(customPath);
}); });
test("getGlobalCasDir respects UNCAGED_CAS_DIR environment variable", () => {
const customPath = join(tmpDir, "legacy-cas");
process.env.UNCAGED_CAS_DIR = customPath;
const casDir = getGlobalCasDir();
expect(casDir).toBe(customPath);
});
test("getGlobalCasDir prefers OCAS_DIR over UNCAGED_CAS_DIR", () => {
process.env.OCAS_DIR = join(tmpDir, "primary-cas");
process.env.UNCAGED_CAS_DIR = join(tmpDir, "legacy-cas");
expect(getGlobalCasDir()).toBe(join(tmpDir, "primary-cas"));
});
test("getGlobalCasDir ignores empty OCAS_DIR", () => { test("getGlobalCasDir ignores empty OCAS_DIR", () => {
process.env.OCAS_DIR = ""; process.env.OCAS_DIR = "";
delete process.env.UNCAGED_CAS_DIR;
const casDir = getGlobalCasDir(); const casDir = getGlobalCasDir();
expect(casDir).toContain(".ocas"); expect(casDir).toContain(".ocas");
}); });
@@ -83,7 +61,7 @@ describe("Global CAS directory", () => {
test("createUwfStore uses global CAS directory", async () => { test("createUwfStore uses global CAS directory", async () => {
const globalCasDir = join(tmpDir, "global-cas"); const globalCasDir = join(tmpDir, "global-cas");
process.env.UNCAGED_CAS_DIR = globalCasDir; process.env.OCAS_DIR = globalCasDir;
const storageRoot = join(tmpDir, "storage"); const storageRoot = join(tmpDir, "storage");
await mkdir(storageRoot, { recursive: true }); await mkdir(storageRoot, { recursive: true });
@@ -104,7 +82,7 @@ describe("Global CAS directory", () => {
test("createUwfStore creates global CAS directory if it does not exist", async () => { test("createUwfStore creates global CAS directory if it does not exist", async () => {
const globalCasDir = join(tmpDir, "new-global-cas"); const globalCasDir = join(tmpDir, "new-global-cas");
process.env.UNCAGED_CAS_DIR = globalCasDir; process.env.OCAS_DIR = globalCasDir;
const storageRoot = join(tmpDir, "storage"); const storageRoot = join(tmpDir, "storage");
await mkdir(storageRoot, { recursive: true }); await mkdir(storageRoot, { recursive: true });
@@ -119,7 +97,7 @@ describe("Global CAS directory", () => {
test("multiple uwfStore instances share the same global CAS filesystem", async () => { test("multiple uwfStore instances share the same global CAS filesystem", async () => {
const globalCasDir = join(tmpDir, "shared-cas"); const globalCasDir = join(tmpDir, "shared-cas");
process.env.UNCAGED_CAS_DIR = globalCasDir; process.env.OCAS_DIR = globalCasDir;
const storageRoot1 = join(tmpDir, "storage1"); const storageRoot1 = join(tmpDir, "storage1");
const storageRoot2 = join(tmpDir, "storage2"); const storageRoot2 = join(tmpDir, "storage2");
@@ -149,7 +127,7 @@ describe("Global CAS directory", () => {
test("workflow registry is stored in global CAS variable store", async () => { test("workflow registry is stored in global CAS variable store", async () => {
const globalCasDir = join(tmpDir, "global-cas"); const globalCasDir = join(tmpDir, "global-cas");
process.env.UNCAGED_CAS_DIR = globalCasDir; process.env.OCAS_DIR = globalCasDir;
const storageRoot = join(tmpDir, "storage"); const storageRoot = join(tmpDir, "storage");
await mkdir(storageRoot, { recursive: true }); await mkdir(storageRoot, { recursive: true });
@@ -170,7 +148,7 @@ describe("Global CAS directory", () => {
test("migrates workflows.yaml to variable store and renames file", async () => { test("migrates workflows.yaml to variable store and renames file", async () => {
const globalCasDir = join(tmpDir, "global-cas"); const globalCasDir = join(tmpDir, "global-cas");
process.env.UNCAGED_CAS_DIR = globalCasDir; process.env.OCAS_DIR = globalCasDir;
const storageRoot = join(tmpDir, "storage-migrate"); const storageRoot = join(tmpDir, "storage-migrate");
await mkdir(storageRoot, { recursive: true }); await mkdir(storageRoot, { recursive: true });
@@ -195,7 +173,7 @@ describe("Global CAS directory", () => {
test("migrates threads.yaml to variable store and renames file", async () => { test("migrates threads.yaml to variable store and renames file", async () => {
const globalCasDir = join(tmpDir, "global-cas-threads"); const globalCasDir = join(tmpDir, "global-cas-threads");
process.env.UNCAGED_CAS_DIR = globalCasDir; process.env.OCAS_DIR = globalCasDir;
const storageRoot = join(tmpDir, "storage-threads-migrate"); const storageRoot = join(tmpDir, "storage-threads-migrate");
await mkdir(storageRoot, { recursive: true }); await mkdir(storageRoot, { recursive: true });
@@ -219,7 +197,7 @@ describe("Global CAS directory", () => {
test("thread metadata stored in ocas variable store", async () => { test("thread metadata stored in ocas variable store", async () => {
const globalCasDir = join(tmpDir, "global-cas"); const globalCasDir = join(tmpDir, "global-cas");
process.env.UNCAGED_CAS_DIR = globalCasDir; process.env.OCAS_DIR = globalCasDir;
const storageRoot = join(tmpDir, "storage"); const storageRoot = join(tmpDir, "storage");
await mkdir(storageRoot, { recursive: true }); await mkdir(storageRoot, { recursive: true });
@@ -240,7 +218,7 @@ describe("Global CAS directory", () => {
test("history is stored in global CAS variable store", async () => { test("history is stored in global CAS variable store", async () => {
const globalCasDir = join(tmpDir, "global-cas"); const globalCasDir = join(tmpDir, "global-cas");
process.env.UNCAGED_CAS_DIR = globalCasDir; process.env.OCAS_DIR = globalCasDir;
const storageRoot = join(tmpDir, "storage"); const storageRoot = join(tmpDir, "storage");
await mkdir(storageRoot, { recursive: true }); await mkdir(storageRoot, { recursive: true });
@@ -271,7 +249,7 @@ describe("Global CAS directory", () => {
test("migrates history.jsonl to variable store and renames file", async () => { test("migrates history.jsonl to variable store and renames file", async () => {
const globalCasDir = join(tmpDir, "global-cas-history"); const globalCasDir = join(tmpDir, "global-cas-history");
process.env.UNCAGED_CAS_DIR = globalCasDir; process.env.OCAS_DIR = globalCasDir;
const storageRoot = join(tmpDir, "storage-history-migrate"); const storageRoot = join(tmpDir, "storage-history-migrate");
await mkdir(storageRoot, { recursive: true }); await mkdir(storageRoot, { recursive: true });
@@ -314,7 +292,7 @@ describe("Global CAS directory", () => {
test("CAS nodes are stored in global directory", async () => { test("CAS nodes are stored in global directory", async () => {
const globalCasDir = join(tmpDir, "global-cas"); const globalCasDir = join(tmpDir, "global-cas");
process.env.UNCAGED_CAS_DIR = globalCasDir; process.env.OCAS_DIR = globalCasDir;
const storageRoot = join(tmpDir, "storage"); const storageRoot = join(tmpDir, "storage");
await mkdir(storageRoot, { recursive: true }); await mkdir(storageRoot, { recursive: true });
@@ -10,13 +10,7 @@ import {
} from "../store.js"; } from "../store.js";
describe("Storage root resolution", () => { describe("Storage root resolution", () => {
const envKeys = [ const envKeys = ["UWF_STORAGE_ROOT", "WORKFLOW_STORAGE_ROOT", "OCAS_DIR"] as const;
"UWF_STORAGE_ROOT",
"WORKFLOW_STORAGE_ROOT",
"UNCAGED_WORKFLOW_STORAGE_ROOT",
"OCAS_DIR",
"UNCAGED_CAS_DIR",
] as const;
const savedEnv: Partial<Record<(typeof envKeys)[number], string | undefined>> = {}; const savedEnv: Partial<Record<(typeof envKeys)[number], string | undefined>> = {};
beforeEach(() => { beforeEach(() => {
@@ -43,36 +37,23 @@ describe("Storage root resolution", () => {
test("resolveStorageRoot prefers UWF_STORAGE_ROOT", () => { test("resolveStorageRoot prefers UWF_STORAGE_ROOT", () => {
process.env.UWF_STORAGE_ROOT = "/tmp/uwf-primary"; process.env.UWF_STORAGE_ROOT = "/tmp/uwf-primary";
process.env.WORKFLOW_STORAGE_ROOT = "/tmp/uwf-fallback"; process.env.WORKFLOW_STORAGE_ROOT = "/tmp/uwf-fallback";
process.env.UNCAGED_WORKFLOW_STORAGE_ROOT = "/tmp/uwf-legacy";
expect(resolveStorageRoot()).toBe("/tmp/uwf-primary"); expect(resolveStorageRoot()).toBe("/tmp/uwf-primary");
}); });
test("resolveStorageRoot falls back to WORKFLOW_STORAGE_ROOT", () => { test("resolveStorageRoot falls back to WORKFLOW_STORAGE_ROOT", () => {
process.env.WORKFLOW_STORAGE_ROOT = "/tmp/uwf-fallback"; process.env.WORKFLOW_STORAGE_ROOT = "/tmp/uwf-fallback";
process.env.UNCAGED_WORKFLOW_STORAGE_ROOT = "/tmp/uwf-legacy";
expect(resolveStorageRoot()).toBe("/tmp/uwf-fallback"); expect(resolveStorageRoot()).toBe("/tmp/uwf-fallback");
}); });
test("resolveStorageRoot falls back to UNCAGED_WORKFLOW_STORAGE_ROOT", () => {
process.env.UNCAGED_WORKFLOW_STORAGE_ROOT = "/tmp/uwf-legacy";
expect(resolveStorageRoot()).toBe("/tmp/uwf-legacy");
});
test("getGlobalCasDir returns ~/.ocas by default", () => { test("getGlobalCasDir returns ~/.ocas by default", () => {
const casDir = getGlobalCasDir(); const casDir = getGlobalCasDir();
expect(casDir).toBe(join(homedir(), ".ocas")); expect(casDir).toBe(join(homedir(), ".ocas"));
}); });
test("getGlobalCasDir prefers OCAS_DIR over UNCAGED_CAS_DIR", () => { test("getGlobalCasDir respects OCAS_DIR", () => {
process.env.OCAS_DIR = "/tmp/ocas-primary"; process.env.OCAS_DIR = "/tmp/ocas-primary";
process.env.UNCAGED_CAS_DIR = "/tmp/ocas-legacy";
expect(getGlobalCasDir()).toBe("/tmp/ocas-primary"); expect(getGlobalCasDir()).toBe("/tmp/ocas-primary");
}); });
test("getGlobalCasDir falls back to UNCAGED_CAS_DIR", () => {
process.env.UNCAGED_CAS_DIR = "/tmp/ocas-legacy";
expect(getGlobalCasDir()).toBe("/tmp/ocas-legacy");
});
}); });
describe("migrateStorageIfNeeded", () => { describe("migrateStorageIfNeeded", () => {
@@ -8,7 +8,7 @@ import { addHistoryEntry, createUwfStore, loadAllHistory } from "../store.js";
async function makeUwfStore(storageRoot: string) { async function makeUwfStore(storageRoot: string) {
const casDir = join(storageRoot, "cas"); const casDir = join(storageRoot, "cas");
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
return createUwfStore(storageRoot); return createUwfStore(storageRoot);
} }
@@ -22,8 +22,8 @@ import {
async function makeUwfStore(storageRoot: string): Promise<UwfStore> { async function makeUwfStore(storageRoot: string): Promise<UwfStore> {
const casDir = join(storageRoot, "cas"); const casDir = join(storageRoot, "cas");
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
// Set UNCAGED_CAS_DIR to use the test's CAS directory // Set OCAS_DIR to use the test's CAS directory
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
return createUwfStore(storageRoot); return createUwfStore(storageRoot);
} }
@@ -19,9 +19,9 @@ describe("Thread and edge location integration", () => {
await mkdir(storageRoot, { recursive: true }); await mkdir(storageRoot, { recursive: true });
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
// Set UNCAGED_CAS_DIR for this test // Set OCAS_DIR for this test
originalEnv = process.env.UNCAGED_CAS_DIR; originalEnv = process.env.OCAS_DIR;
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
} }
async function teardown() { async function teardown() {
@@ -30,9 +30,9 @@ describe("Thread and edge location integration", () => {
} }
// Restore original environment // Restore original environment
if (originalEnv === undefined) { if (originalEnv === undefined) {
delete process.env.UNCAGED_CAS_DIR; delete process.env.OCAS_DIR;
} else { } else {
process.env.UNCAGED_CAS_DIR = originalEnv; process.env.OCAS_DIR = originalEnv;
} }
} }
@@ -71,17 +71,17 @@ let originalEnv: string | undefined;
beforeEach(async () => { beforeEach(async () => {
tmpDir = await mkdtemp(join(tmpdir(), "cli-uwf-quota-test-")); tmpDir = await mkdtemp(join(tmpdir(), "cli-uwf-quota-test-"));
originalEnv = process.env.UNCAGED_CAS_DIR; originalEnv = process.env.OCAS_DIR;
process.env.UNCAGED_CAS_DIR = join(tmpDir, "cas"); process.env.OCAS_DIR = join(tmpDir, "cas");
await mkdir(process.env.UNCAGED_CAS_DIR, { recursive: true }); await mkdir(process.env.OCAS_DIR, { recursive: true });
}); });
afterEach(async () => { afterEach(async () => {
await rm(tmpDir, { recursive: true, force: true }); await rm(tmpDir, { recursive: true, force: true });
if (originalEnv === undefined) { if (originalEnv === undefined) {
delete process.env.UNCAGED_CAS_DIR; delete process.env.OCAS_DIR;
} else { } else {
process.env.UNCAGED_CAS_DIR = originalEnv; process.env.OCAS_DIR = originalEnv;
} }
}); });
@@ -53,7 +53,7 @@ const DETAIL_SCHEMA = {
async function makeUwfStore(storageRoot: string): Promise<UwfStore> { async function makeUwfStore(storageRoot: string): Promise<UwfStore> {
const casDir = join(storageRoot, "cas"); const casDir = join(storageRoot, "cas");
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
return createUwfStore(storageRoot); return createUwfStore(storageRoot);
} }
@@ -88,7 +88,7 @@ async function setupSuspendedThread(mode: MockAgentMode): Promise<{
cwd: tmpDir, cwd: tmpDir,
}); });
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
await seedThreads(tmpDir, { [THREAD_ID]: startHash }); await seedThreads(tmpDir, { [THREAD_ID]: startHash });
const outputHash = await store.put(outputSchemaHash, { const outputHash = await store.put(outputSchemaHash, {
@@ -189,7 +189,7 @@ function runUwf(
env: { env: {
...process.env, ...process.env,
WORKFLOW_STORAGE_ROOT: tmpDir, WORKFLOW_STORAGE_ROOT: tmpDir,
UNCAGED_CAS_DIR: casDir, OCAS_DIR: casDir,
}, },
cwd: tmpDir, cwd: tmpDir,
timeout: 30000, timeout: 30000,
@@ -241,7 +241,7 @@ describe("uwf thread resume", () => {
cwd: tmpDir, cwd: tmpDir,
}); });
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
await seedThreads(tmpDir, { [THREAD_ID]: startHash }); await seedThreads(tmpDir, { [THREAD_ID]: startHash });
const result = runUwf(["thread", "resume", THREAD_ID], casDir); const result = runUwf(["thread", "resume", THREAD_ID], casDir);
@@ -250,9 +250,9 @@ describe("uwf thread resume", () => {
}); });
test("resume suspended thread executes step and becomes idle", async () => { test("resume suspended thread executes step and becomes idle", async () => {
const originalCasDir = process.env.UNCAGED_CAS_DIR; const originalCasDir = process.env.OCAS_DIR;
const { casDir, mockAgentPath } = await setupSuspendedThread("ok"); const { casDir, mockAgentPath } = await setupSuspendedThread("ok");
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
try { try {
const result = runUwf(["thread", "resume", THREAD_ID, "--agent", mockAgentPath], casDir); const result = runUwf(["thread", "resume", THREAD_ID, "--agent", mockAgentPath], casDir);
@@ -278,17 +278,17 @@ describe("uwf thread resume", () => {
expect(showResult.suspendMessage).toBeNull(); expect(showResult.suspendMessage).toBeNull();
} finally { } finally {
if (originalCasDir === undefined) { if (originalCasDir === undefined) {
delete process.env.UNCAGED_CAS_DIR; delete process.env.OCAS_DIR;
} else { } else {
process.env.UNCAGED_CAS_DIR = originalCasDir; process.env.OCAS_DIR = originalCasDir;
} }
} }
}); });
test("resume without -p uses suspend message as agent prompt", async () => { test("resume without -p uses suspend message as agent prompt", async () => {
const originalCasDir = process.env.UNCAGED_CAS_DIR; const originalCasDir = process.env.OCAS_DIR;
const { casDir, mockAgentPath, promptCapturePath } = await setupSuspendedThread("ok"); const { casDir, mockAgentPath, promptCapturePath } = await setupSuspendedThread("ok");
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
try { try {
const result = runUwf(["thread", "resume", THREAD_ID, "--agent", mockAgentPath], casDir); const result = runUwf(["thread", "resume", THREAD_ID, "--agent", mockAgentPath], casDir);
@@ -298,17 +298,17 @@ describe("uwf thread resume", () => {
expect(capturedPrompt).toBe(SUSPEND_MESSAGE); expect(capturedPrompt).toBe(SUSPEND_MESSAGE);
} finally { } finally {
if (originalCasDir === undefined) { if (originalCasDir === undefined) {
delete process.env.UNCAGED_CAS_DIR; delete process.env.OCAS_DIR;
} else { } else {
process.env.UNCAGED_CAS_DIR = originalCasDir; process.env.OCAS_DIR = originalCasDir;
} }
} }
}); });
test("resume with -p appends supplementary info to agent prompt", async () => { test("resume with -p appends supplementary info to agent prompt", async () => {
const originalCasDir = process.env.UNCAGED_CAS_DIR; const originalCasDir = process.env.OCAS_DIR;
const { casDir, mockAgentPath, promptCapturePath } = await setupSuspendedThread("ok"); const { casDir, mockAgentPath, promptCapturePath } = await setupSuspendedThread("ok");
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
try { try {
const supplement = "Use the REST API."; const supplement = "Use the REST API.";
@@ -322,17 +322,17 @@ describe("uwf thread resume", () => {
expect(capturedPrompt).toBe(`${SUSPEND_MESSAGE}\n\n${supplement}`); expect(capturedPrompt).toBe(`${SUSPEND_MESSAGE}\n\n${supplement}`);
} finally { } finally {
if (originalCasDir === undefined) { if (originalCasDir === undefined) {
delete process.env.UNCAGED_CAS_DIR; delete process.env.OCAS_DIR;
} else { } else {
process.env.UNCAGED_CAS_DIR = originalCasDir; process.env.OCAS_DIR = originalCasDir;
} }
} }
}); });
test("multiple suspend/resume cycles", async () => { test("multiple suspend/resume cycles", async () => {
const originalCasDir = process.env.UNCAGED_CAS_DIR; const originalCasDir = process.env.OCAS_DIR;
const { casDir, mockAgentPath, promptCapturePath } = await setupSuspendedThread("suspend"); const { casDir, mockAgentPath, promptCapturePath } = await setupSuspendedThread("suspend");
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
try { try {
const firstResult = runUwf(["thread", "resume", THREAD_ID, "--agent", mockAgentPath], casDir); const firstResult = runUwf(["thread", "resume", THREAD_ID, "--agent", mockAgentPath], casDir);
@@ -370,9 +370,9 @@ describe("uwf thread resume", () => {
expect(capturedPrompt).toBe(SUSPEND_MESSAGE); expect(capturedPrompt).toBe(SUSPEND_MESSAGE);
} finally { } finally {
if (originalCasDir === undefined) { if (originalCasDir === undefined) {
delete process.env.UNCAGED_CAS_DIR; delete process.env.OCAS_DIR;
} else { } else {
process.env.UNCAGED_CAS_DIR = originalCasDir; process.env.OCAS_DIR = originalCasDir;
} }
} }
}); });
@@ -305,8 +305,8 @@ describe("thread show status field", () => {
await setupTestEnv(); await setupTestEnv();
const casDir = join(tmpDir, "cas"); const casDir = join(tmpDir, "cas");
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
const originalCasDir = process.env.UNCAGED_CAS_DIR; const originalCasDir = process.env.OCAS_DIR;
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
try { try {
const workflowPath = join(tmpDir, "test-suspend-status.yaml"); const workflowPath = join(tmpDir, "test-suspend-status.yaml");
@@ -331,9 +331,9 @@ describe("thread show status field", () => {
expect(result.thread).toBe(threadId); expect(result.thread).toBe(threadId);
} finally { } finally {
if (originalCasDir === undefined) { if (originalCasDir === undefined) {
delete process.env.UNCAGED_CAS_DIR; delete process.env.OCAS_DIR;
} else { } else {
process.env.UNCAGED_CAS_DIR = originalCasDir; process.env.OCAS_DIR = originalCasDir;
} }
await teardown(); await teardown();
} }
@@ -20,9 +20,9 @@ describe("thread start --cwd CLI option", () => {
await mkdir(storageRoot, { recursive: true }); await mkdir(storageRoot, { recursive: true });
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
// Set UNCAGED_CAS_DIR for this test // Set OCAS_DIR for this test
originalEnv = process.env.UNCAGED_CAS_DIR; originalEnv = process.env.OCAS_DIR;
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
} }
async function teardown() { async function teardown() {
@@ -31,9 +31,9 @@ describe("thread start --cwd CLI option", () => {
} }
// Restore original environment // Restore original environment
if (originalEnv === undefined) { if (originalEnv === undefined) {
delete process.env.UNCAGED_CAS_DIR; delete process.env.OCAS_DIR;
} else { } else {
process.env.UNCAGED_CAS_DIR = originalEnv; process.env.OCAS_DIR = originalEnv;
} }
} }
@@ -137,7 +137,7 @@ graph:
// Register the workflow // Register the workflow
execFileSync("bun", [uwfBin, "workflow", "add", workflowPath], { execFileSync("bun", [uwfBin, "workflow", "add", workflowPath], {
env: { ...process.env, UWF_STORAGE_ROOT: storageRoot, UNCAGED_CAS_DIR: casDir }, env: { ...process.env, UWF_STORAGE_ROOT: storageRoot, OCAS_DIR: casDir },
encoding: "utf8", encoding: "utf8",
}); });
@@ -146,7 +146,7 @@ graph:
"bun", "bun",
[uwfBin, "thread", "start", "test-cwd-cli", "-p", "test prompt", "--cwd", testCwd], [uwfBin, "thread", "start", "test-cwd-cli", "-p", "test prompt", "--cwd", testCwd],
{ {
env: { ...process.env, UWF_STORAGE_ROOT: storageRoot, UNCAGED_CAS_DIR: casDir }, env: { ...process.env, UWF_STORAGE_ROOT: storageRoot, OCAS_DIR: casDir },
encoding: "utf8", encoding: "utf8",
}, },
); );
@@ -34,8 +34,8 @@ describe("suspend step CAS chain and threads.yaml metadata", () => {
test("thread exec records suspend step in CAS and suspend metadata in threads.yaml", async () => { test("thread exec records suspend step in CAS and suspend metadata in threads.yaml", async () => {
const casDir = join(tmpDir, "cas"); const casDir = join(tmpDir, "cas");
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
const originalCasDir = process.env.UNCAGED_CAS_DIR; const originalCasDir = process.env.OCAS_DIR;
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
try { try {
const store = createFsStore(casDir); const store = createFsStore(casDir);
@@ -128,7 +128,7 @@ describe("suspend step CAS chain and threads.yaml metadata", () => {
env: { env: {
...process.env, ...process.env,
WORKFLOW_STORAGE_ROOT: tmpDir, WORKFLOW_STORAGE_ROOT: tmpDir,
UNCAGED_CAS_DIR: casDir, OCAS_DIR: casDir,
}, },
cwd: tmpDir, cwd: tmpDir,
timeout: 30000, timeout: 30000,
@@ -169,9 +169,9 @@ describe("suspend step CAS chain and threads.yaml metadata", () => {
expect(showResult.suspendedRole).toBe("worker"); expect(showResult.suspendedRole).toBe("worker");
} finally { } finally {
if (originalCasDir === undefined) { if (originalCasDir === undefined) {
delete process.env.UNCAGED_CAS_DIR; delete process.env.OCAS_DIR;
} else { } else {
process.env.UNCAGED_CAS_DIR = originalCasDir; process.env.OCAS_DIR = originalCasDir;
} }
} }
}); });
@@ -33,8 +33,8 @@ describe("suspended thread display", () => {
test("thread list shows [suspended] marker for suspended threads", async () => { test("thread list shows [suspended] marker for suspended threads", async () => {
const casDir = join(tmpDir, "cas"); const casDir = join(tmpDir, "cas");
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
const originalCasDir = process.env.UNCAGED_CAS_DIR; const originalCasDir = process.env.OCAS_DIR;
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
try { try {
const uwf = await createUwfStore(tmpDir); const uwf = await createUwfStore(tmpDir);
@@ -131,9 +131,9 @@ describe("suspended thread display", () => {
expect(idleItem!.statusDisplay).toBe("idle"); expect(idleItem!.statusDisplay).toBe("idle");
} finally { } finally {
if (originalCasDir === undefined) { if (originalCasDir === undefined) {
delete process.env.UNCAGED_CAS_DIR; delete process.env.OCAS_DIR;
} else { } else {
process.env.UNCAGED_CAS_DIR = originalCasDir; process.env.OCAS_DIR = originalCasDir;
} }
} }
}); });
@@ -141,8 +141,8 @@ describe("suspended thread display", () => {
test("thread show displays suspend info and resume hint", async () => { test("thread show displays suspend info and resume hint", async () => {
const casDir = join(tmpDir, "cas"); const casDir = join(tmpDir, "cas");
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
const originalCasDir = process.env.UNCAGED_CAS_DIR; const originalCasDir = process.env.OCAS_DIR;
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
try { try {
const uwf = await createUwfStore(tmpDir); const uwf = await createUwfStore(tmpDir);
@@ -219,9 +219,9 @@ describe("suspended thread display", () => {
); );
} finally { } finally {
if (originalCasDir === undefined) { if (originalCasDir === undefined) {
delete process.env.UNCAGED_CAS_DIR; delete process.env.OCAS_DIR;
} else { } else {
process.env.UNCAGED_CAS_DIR = originalCasDir; process.env.OCAS_DIR = originalCasDir;
} }
} }
}); });
@@ -229,8 +229,8 @@ describe("suspended thread display", () => {
test("non-suspended threads do not show suspend markers or hints", async () => { test("non-suspended threads do not show suspend markers or hints", async () => {
const casDir = join(tmpDir, "cas"); const casDir = join(tmpDir, "cas");
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
const originalCasDir = process.env.UNCAGED_CAS_DIR; const originalCasDir = process.env.OCAS_DIR;
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
try { try {
const uwf = await createUwfStore(tmpDir); const uwf = await createUwfStore(tmpDir);
@@ -278,9 +278,9 @@ describe("suspended thread display", () => {
expect(threadItem!.statusDisplay).toBe("idle"); expect(threadItem!.statusDisplay).toBe("idle");
} finally { } finally {
if (originalCasDir === undefined) { if (originalCasDir === undefined) {
delete process.env.UNCAGED_CAS_DIR; delete process.env.OCAS_DIR;
} else { } else {
process.env.UNCAGED_CAS_DIR = originalCasDir; process.env.OCAS_DIR = originalCasDir;
} }
} }
}); });
+1 -1
View File
@@ -58,7 +58,7 @@ const DETAIL_SCHEMA = {
async function makeUwfStore(storageRoot: string): Promise<UwfStore> { async function makeUwfStore(storageRoot: string): Promise<UwfStore> {
const casDir = join(storageRoot, "cas"); const casDir = join(storageRoot, "cas");
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
return createUwfStore(storageRoot); return createUwfStore(storageRoot);
} }
@@ -13,7 +13,7 @@ import { createUwfStore, saveWorkflowRegistry } from "../store.js";
async function makeUwfStore(storageRoot: string): Promise<UwfStore> { async function makeUwfStore(storageRoot: string): Promise<UwfStore> {
const casDir = join(storageRoot, "cas"); const casDir = join(storageRoot, "cas");
await mkdir(casDir, { recursive: true }); await mkdir(casDir, { recursive: true });
process.env.UNCAGED_CAS_DIR = casDir; process.env.OCAS_DIR = casDir;
return createUwfStore(storageRoot); return createUwfStore(storageRoot);
} }
+4 -12
View File
@@ -119,7 +119,7 @@ export function getDefaultStorageRoot(): string {
/** /**
* Resolve storage root. * Resolve storage root.
* Priority: `UWF_STORAGE_ROOT` `WORKFLOW_STORAGE_ROOT` `UNCAGED_WORKFLOW_STORAGE_ROOT` (legacy) default. * Priority: `UWF_STORAGE_ROOT` `WORKFLOW_STORAGE_ROOT` default.
*/ */
export function resolveStorageRoot(): string { export function resolveStorageRoot(): string {
const primary = process.env.UWF_STORAGE_ROOT; const primary = process.env.UWF_STORAGE_ROOT;
@@ -130,10 +130,6 @@ export function resolveStorageRoot(): string {
if (userOverride !== undefined && userOverride !== "") { if (userOverride !== undefined && userOverride !== "") {
return userOverride; return userOverride;
} }
const legacy = process.env.UNCAGED_WORKFLOW_STORAGE_ROOT;
if (legacy !== undefined && legacy !== "") {
return legacy;
}
return getDefaultStorageRoot(); return getDefaultStorageRoot();
} }
@@ -145,7 +141,7 @@ export function migrateStorageIfNeeded(home: string = homedir()): void {
if (!existsSync(newPath) && existsSync(oldPath)) { if (!existsSync(newPath) && existsSync(oldPath)) {
symlinkSync(oldPath, newPath); symlinkSync(oldPath, newPath);
// biome-ignore lint/suspicious/noConsole: migration notice // biome-ignore lint/suspicious/noConsole: migration notice
console.log("⚠️ Storage migrated: ~/.uwf → ~/.uncaged/workflow (symlink)"); console.log("⚠️ Storage linked: ~/.uwf → legacy workflow directory (symlink)");
// biome-ignore lint/suspicious/noConsole: migration notice // biome-ignore lint/suspicious/noConsole: migration notice
console.log( console.log(
" This symlink is temporary. Copy your data to ~/.uwf/ and remove the symlink in a future version.", " This symlink is temporary. Copy your data to ~/.uwf/ and remove the symlink in a future version.",
@@ -157,7 +153,7 @@ export function migrateStorageIfNeeded(home: string = homedir()): void {
if (!existsSync(newCas) && existsSync(oldCas)) { if (!existsSync(newCas) && existsSync(oldCas)) {
symlinkSync(oldCas, newCas); symlinkSync(oldCas, newCas);
// biome-ignore lint/suspicious/noConsole: migration notice // biome-ignore lint/suspicious/noConsole: migration notice
console.log("⚠️ CAS storage migrated: ~/.ocas → ~/.uncaged/json-cas (symlink)"); console.log("⚠️ CAS storage linked: ~/.ocas → legacy CAS directory (symlink)");
} }
} }
@@ -171,17 +167,13 @@ export function getCasDir(storageRoot: string): string {
/** /**
* Returns the global CAS directory shared by all uwf and ocas tools. * Returns the global CAS directory shared by all uwf and ocas tools.
* Priority: `OCAS_DIR` `UNCAGED_CAS_DIR` (legacy) default ~/.ocas * Priority: `OCAS_DIR` default ~/.ocas
*/ */
export function getGlobalCasDir(): string { export function getGlobalCasDir(): string {
const primary = process.env.OCAS_DIR; const primary = process.env.OCAS_DIR;
if (primary !== undefined && primary !== "") { if (primary !== undefined && primary !== "") {
return primary; return primary;
} }
const legacy = process.env.UNCAGED_CAS_DIR;
if (legacy !== undefined && legacy !== "") {
return legacy;
}
return join(homedir(), ".ocas"); return join(homedir(), ".ocas");
} }
+3 -3
View File
@@ -31,12 +31,12 @@
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://git.shazhou.work/uncaged/workflow.git", "url": "https://git.shazhou.work/shazhou/united-workforce.git",
"directory": "packages/protocol" "directory": "packages/protocol"
}, },
"homepage": "https://git.shazhou.work/uncaged/workflow#readme", "homepage": "https://git.shazhou.work/shazhou/united-workforce#readme",
"bugs": { "bugs": {
"url": "https://git.shazhou.work/uncaged/workflow/issues" "url": "https://git.shazhou.work/shazhou/united-workforce/issues"
}, },
"license": "MIT" "license": "MIT"
} }
+1 -1
View File
@@ -180,4 +180,4 @@ src/
## Configuration ## Configuration
Reads `config.yaml` and `.env` from the workflow storage root (`~/.uncaged/workflow` by default). See `@united-workforce/protocol` for `WorkflowConfig` shape. Set via `uwf setup`. Reads `config.yaml` and `.env` from the workflow storage root (`~/.uwf` by default). See `@united-workforce/protocol` for `WorkflowConfig` shape. Set via `uwf setup`.
+3 -3
View File
@@ -35,12 +35,12 @@
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://git.shazhou.work/uncaged/workflow.git", "url": "https://git.shazhou.work/shazhou/united-workforce.git",
"directory": "packages/util-agent" "directory": "packages/util-agent"
}, },
"homepage": "https://git.shazhou.work/uncaged/workflow#readme", "homepage": "https://git.shazhou.work/shazhou/united-workforce#readme",
"bugs": { "bugs": {
"url": "https://git.shazhou.work/uncaged/workflow/issues" "url": "https://git.shazhou.work/shazhou/united-workforce/issues"
}, },
"license": "MIT" "license": "MIT"
} }
+2 -10
View File
@@ -29,7 +29,7 @@ export function getDefaultStorageRoot(): string {
/** /**
* Resolve storage root. * Resolve storage root.
* Priority: `UWF_STORAGE_ROOT` `WORKFLOW_STORAGE_ROOT` `UNCAGED_WORKFLOW_STORAGE_ROOT` (legacy) default. * Priority: `UWF_STORAGE_ROOT` `WORKFLOW_STORAGE_ROOT` default.
*/ */
export function resolveStorageRoot(): string { export function resolveStorageRoot(): string {
const primary = process.env.UWF_STORAGE_ROOT; const primary = process.env.UWF_STORAGE_ROOT;
@@ -40,10 +40,6 @@ export function resolveStorageRoot(): string {
if (userOverride !== undefined && userOverride !== "") { if (userOverride !== undefined && userOverride !== "") {
return userOverride; return userOverride;
} }
const legacy = process.env.UNCAGED_WORKFLOW_STORAGE_ROOT;
if (legacy !== undefined && legacy !== "") {
return legacy;
}
return getDefaultStorageRoot(); return getDefaultStorageRoot();
} }
@@ -63,17 +59,13 @@ const THREAD_VAR_PREFIX = "@uwf/thread/";
/** /**
* Global CAS directory (same as uwf CLI). * Global CAS directory (same as uwf CLI).
* Priority: `OCAS_DIR` `UNCAGED_CAS_DIR` (legacy) default ~/.ocas * Priority: `OCAS_DIR` default ~/.ocas
*/ */
export function getGlobalCasDir(): string { export function getGlobalCasDir(): string {
const primary = process.env.OCAS_DIR; const primary = process.env.OCAS_DIR;
if (primary !== undefined && primary !== "") { if (primary !== undefined && primary !== "") {
return primary; return primary;
} }
const legacy = process.env.UNCAGED_CAS_DIR;
if (legacy !== undefined && legacy !== "") {
return legacy;
}
return join(homedir(), ".ocas"); return join(homedir(), ".ocas");
} }
+2 -2
View File
@@ -135,7 +135,7 @@ src/
├── frontmatter-markdown/ Parse and validate agent frontmatter ├── frontmatter-markdown/ Parse and validate agent frontmatter
├── refs-field.ts Normalize refs arrays on CAS nodes ├── refs-field.ts Normalize refs arrays on CAS nodes
├── result.ts ok / err helpers ├── result.ts ok / err helpers
├── storage-root.ts Default ~/.uncaged/workflow paths ├── storage-root.ts Default ~/.uwf paths
├── env.ts Environment variable helper ├── env.ts Environment variable helper
├── cli-reference.ts Markdown CLI reference generator ├── cli-reference.ts Markdown CLI reference generator
└── types.ts LogFn, Result, logger options └── types.ts LogFn, Result, logger options
@@ -143,4 +143,4 @@ src/
## Configuration ## Configuration
`getDefaultWorkflowStorageRoot()` resolves to `~/.uncaged/workflow` unless overridden by environment (see `storage-root.ts`). `getDefaultWorkflowStorageRoot()` resolves to `~/.uwf` unless overridden by environment (see `storage-root.ts`).
+3 -3
View File
@@ -28,12 +28,12 @@
}, },
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://git.shazhou.work/uncaged/workflow.git", "url": "https://git.shazhou.work/shazhou/united-workforce.git",
"directory": "packages/util" "directory": "packages/util"
}, },
"homepage": "https://git.shazhou.work/uncaged/workflow#readme", "homepage": "https://git.shazhou.work/shazhou/united-workforce#readme",
"bugs": { "bugs": {
"url": "https://git.shazhou.work/uncaged/workflow/issues" "url": "https://git.shazhou.work/shazhou/united-workforce/issues"
}, },
"license": "MIT" "license": "MIT"
} }
+3 -3
View File
@@ -1,15 +1,15 @@
export function generateBootstrapReference(): string { export function generateBootstrapReference(): string {
return `--- return `---
name: uwf name: uwf
description: "Uncaged Workflow (uwf) — YAML 状态机工作流引擎。任务涉及 workflow 时加载此 skill。" description: "United Workforce (uwf) — YAML 状态机工作流引擎。任务涉及 workflow 时加载此 skill。"
tags: [workflow, uwf, uncaged] tags: [workflow, uwf]
triggers: triggers:
- uwf - uwf
- workflow - workflow
- -
--- ---
# uwf (Uncaged Workflow) # uwf (United Workforce)
YAML workflow **uwf workflow**YAML Hermes skill \`uwf\` CLI 操作,不要混淆。 YAML workflow **uwf workflow**YAML Hermes skill \`uwf\` CLI 操作,不要混淆。
+2 -2
View File
@@ -7,12 +7,12 @@
# Examples: # Examples:
# ./scripts/batch-solve.sh 448 449 # ./scripts/batch-solve.sh 448 449
# ./scripts/batch-solve.sh --agent "bun run $(pwd)/packages/agent-claude-code/src/cli.ts" 448 449 # ./scripts/batch-solve.sh --agent "bun run $(pwd)/packages/agent-claude-code/src/cli.ts" 448 449
# ./scripts/batch-solve.sh --repo uncaged/workflow --count 15 448 449 # ./scripts/batch-solve.sh --repo shazhou/united-workforce --count 15 448 449
set -euo pipefail set -euo pipefail
AGENT="" AGENT=""
REPO="uncaged/workflow" REPO="shazhou/united-workforce"
COUNT=10 COUNT=10
ISSUES=() ISSUES=()
+3 -3
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# Check development environment prerequisites for uncaged/workflow. # Check development environment prerequisites for shazhou/united-workforce.
# Non-interactive — prints actionable fix instructions on failure. # Non-interactive — prints actionable fix instructions on failure.
# Exit 0 = all good, exit 1 = missing dependencies. # Exit 0 = all good, exit 1 = missing dependencies.
set -euo pipefail set -euo pipefail
@@ -54,7 +54,7 @@ echo "=== Workflow ==="
REPO_DIR="${WORKFLOW_REPO:-$(cd "$(dirname "$0")/.." && pwd)}" REPO_DIR="${WORKFLOW_REPO:-$(cd "$(dirname "$0")/.." && pwd)}"
check "repo at ~/repos/workflow or WORKFLOW_REPO set" \ check "repo at ~/repos/workflow or WORKFLOW_REPO set" \
"[ -f '$REPO_DIR/packages/cli/src/cli.ts' ]" \ "[ -f '$REPO_DIR/packages/cli/src/cli.ts' ]" \
"Clone the repo: git clone https://git.shazhou.work/uncaged/workflow ~/repos/workflow" "Clone the repo: git clone https://git.shazhou.work/shazhou/united-workforce ~/repos/workflow"
# Check bun install # Check bun install
check "node_modules installed" \ check "node_modules installed" \
@@ -89,7 +89,7 @@ echo ""
echo "=== Config ===" echo "=== Config ==="
# Check workflow config exists # Check workflow config exists
CONFIG_DIR="${UNCAGED_WORKFLOW_STORAGE_ROOT:-$HOME/.uncaged/workflow}" CONFIG_DIR="${UWF_STORAGE_ROOT:-$HOME/.shazhou/united-workforce}"
check "config.yaml exists" \ check "config.yaml exists" \
"[ -f '$CONFIG_DIR/config.yaml' ]" \ "[ -f '$CONFIG_DIR/config.yaml' ]" \
"Run: uwf setup" "Run: uwf setup"
+7 -7
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env bash #!/usr/bin/env bash
# E2E walkthrough for uncaged/workflow. # E2E walkthrough for shazhou/united-workforce.
# Runs inside Docker with isolated UNCAGED_WORKFLOW_STORAGE_ROOT. # Runs inside Docker with isolated UWF_STORAGE_ROOT.
# Exercises: setup → workflow add → thread start/exec → cancel/fork → read/inspect. # Exercises: setup → workflow add → thread start/exec → cancel/fork → read/inspect.
# #
# Usage: # Usage:
@@ -69,9 +69,9 @@ cat > "$E2E_DIR/run.sh" << 'INNER_SCRIPT'
#!/usr/bin/env bash #!/usr/bin/env bash
set -euo pipefail set -euo pipefail
# Isolated storage — never touches host's ~/.uncaged/workflow # Isolated storage — never touches host's ~/.uwf
export UNCAGED_WORKFLOW_STORAGE_ROOT="/tmp/uwf-e2e-storage" export UWF_STORAGE_ROOT="/tmp/uwf-e2e-storage"
mkdir -p "$UNCAGED_WORKFLOW_STORAGE_ROOT" mkdir -p "$UWF_STORAGE_ROOT"
REPO_DIR="$1" REPO_DIR="$1"
AGENT="$2" AGENT="$2"
@@ -156,8 +156,8 @@ if [ -n "$PROVIDER" ] && [ -n "$MODEL" ] && [ -n "$API_KEY" ]; then
run_test "uwf setup (non-interactive)" bash -c "$SETUP_CMD" run_test "uwf setup (non-interactive)" bash -c "$SETUP_CMD"
else else
# Copy host config if available # Copy host config if available
if [ -f "$HOME/.uncaged/workflow/config.yaml" ]; then if [ -f "$HOME/.shazhou/united-workforce/config.yaml" ]; then
cp "$HOME/.uncaged/workflow/config.yaml" "$UNCAGED_WORKFLOW_STORAGE_ROOT/config.yaml" cp "$HOME/.shazhou/united-workforce/config.yaml" "$UWF_STORAGE_ROOT/config.yaml"
echo " Copied host config.yaml" >&2 echo " Copied host config.yaml" >&2
fi fi
fi fi
+3 -3
View File
@@ -1,14 +1,14 @@
--- ---
name: uwf name: uwf
description: "Uncaged Workflow (uwf) — YAML 状态机工作流引擎。任务涉及 workflow 时加载此 skill。" description: "United Workforce (uwf) — YAML 状态机工作流引擎。任务涉及 workflow 时加载此 skill。"
tags: [workflow, uwf, uncaged] tags: [workflow, uwf]
triggers: triggers:
- uwf - uwf
- workflow - workflow
- 工作流 - 工作流
--- ---
# uwf (Uncaged Workflow) # uwf (United Workforce)
YAML 状态机工作流引擎。当用户提到「workflow」「工作流」时,指的是 **uwf workflow**(YAML 定义的状态机),不是 Hermes skill。用 `uwf` CLI 操作,不要混淆。 YAML 状态机工作流引擎。当用户提到「workflow」「工作流」时,指的是 **uwf workflow**(YAML 定义的状态机),不是 Hermes skill。用 `uwf` CLI 操作,不要混淆。
+3 -3
View File
@@ -742,7 +742,7 @@ roles:
6. Private packages (server, frontend, tools) must have `"private": true` and can skip publishConfig/files 6. Private packages (server, frontend, tools) must have `"private": true` and can skip publishConfig/files
7. Each package should have a `"scripts"` section with at least `"test"` if tests exist 7. Each package should have a `"scripts"` section with at least `"test"` if tests exist
8. Workspace dependencies should use `"workspace:^"` protocol, not version numbers 8. Workspace dependencies should use `"workspace:^"` protocol, not version numbers
- Check: `grep -r '"@uncaged/' packages/*/package.json | grep -v 'workspace:'` - Check: `grep -r '"@united-workforce/' packages/*/package.json | grep -v 'workspace:'`
## Verification ## Verification
You MUST actually run each command below and include real output. Do NOT guess or fabricate results. You MUST actually run each command below and include real output. Do NOT guess or fabricate results.
@@ -764,7 +764,7 @@ roles:
" "
done done
# No hardcoded workspace deps # No hardcoded workspace deps
! grep -r '"@uncaged/' packages/*/package.json | grep -v 'workspace:' | grep -v node_modules ! grep -r '"@united-workforce/' packages/*/package.json | grep -v 'workspace:' | grep -v node_modules
``` ```
Post-condition: Verification script shows OK for all non-private packages, no hardcoded workspace versions. Post-condition: Verification script shows OK for all non-private packages, no hardcoded workspace versions.
@@ -849,4 +849,4 @@ roles:
type: string type: string
capabilities: capabilities:
- workflow-config - workflow-config
description: Normalize an existing project to @uncaged bun monorepo conventions. Supports both TypeScript and JS/MJS projects. Each role handles one configuration layer. All roles allow fail. description: Normalize an existing project to @united-workforce bun monorepo conventions. Supports both TypeScript and JS/MJS projects. Each role handles one configuration layer. All roles allow fail.
+2 -2
View File
@@ -148,7 +148,7 @@ roles:
- Crockford Base32 log tags (8-char, unique per call site) - Crockford Base32 log tags (8-char, unique per call site)
- No `console.log` in production code (use createLogger from @uncaged/util) - No `console.log` in production code (use createLogger from @united-workforce/util)
- No dynamic imports in production code - No dynamic imports in production code
@@ -247,7 +247,7 @@ roles:
\ create a new branch.\n1. Stage all changes: `git add -A`\n2. Commit with a descriptive message referencing the issue: `git commit -m \"type: description\\n\\nFixes #N\"`\n3. Push the branch: `git\ \ create a new branch.\n1. Stage all changes: `git add -A`\n2. Commit with a descriptive message referencing the issue: `git commit -m \"type: description\\n\\nFixes #N\"`\n3. Push the branch: `git\
\ push -u origin <branch-name>`\n4. **Verify push succeeded** — run `git ls-remote origin <branch-name>` and confirm it prints a commit hash.\n - If no output or push failed: capture the error, mark hook_failed\n\ \ push -u origin <branch-name>`\n4. **Verify push succeeded** — run `git ls-remote origin <branch-name>` and confirm it prints a commit hash.\n - If no output or push failed: capture the error, mark hook_failed\n\
5. Create a PR using the Gitea API (do NOT use `tea pr create` — it fails in worktrees):\n ```bash\n GITEA_TOKEN=$(cfg get GITEA_TOKEN)\n curl -s -X POST -H \"Authorization: token $GITEA_TOKEN\" -H \"Content-Type: application/json\" \\\n\ 5. Create a PR using the Gitea API (do NOT use `tea pr create` — it fails in worktrees):\n ```bash\n GITEA_TOKEN=$(cfg get GITEA_TOKEN)\n curl -s -X POST -H \"Authorization: token $GITEA_TOKEN\" -H \"Content-Type: application/json\" \\\n\
\ \"https://git.shazhou.work/api/v1/repos/<owner>/<repo>/pulls\" \\\n -d '{\"title\":\"...\",\"body\":\"...\",\"head\":\"<branch>\",\"base\":\"main\"}'\n ```\n - The repo remote (owner/repo format, e.g. \"uncaged/workflow\") is given in your task prompt — use it directly.\n\ \ \"https://git.shazhou.work/api/v1/repos/<owner>/<repo>/pulls\" \\\n -d '{\"title\":\"...\",\"body\":\"...\",\"head\":\"<branch>\",\"base\":\"main\"}'\n ```\n - The repo remote (owner/repo format, e.g. \"shazhou/united-workforce\") is given in your task prompt — use it directly.\n\
\ - PR body must include: What / Why / Changes / Ref sections, with `Fixes #N` in Ref\n6. **Verify PR was created** — parse the curl response JSON: it must contain a `\"number\"` field. Print the PR URL.\n\ \ - PR body must include: What / Why / Changes / Ref sections, with `Fixes #N` in Ref\n6. **Verify PR was created** — parse the curl response JSON: it must contain a `\"number\"` field. Print the PR URL.\n\
\ - If curl returns an error or no number field: capture the response, mark hook_failed\n7. After PR creation, clean up the worktree:\n - cd to the repo root (parent of .worktrees)\n - `git worktree remove <worktree-path>`" \ - If curl returns an error or no number field: capture the response, mark hook_failed\n7. After PR creation, clean up the worktree:\n - cd to the repo root (parent of .worktrees)\n - `git worktree remove <worktree-path>`"
output: Include PR URL on success or error log on failure. Set $status to committed (with prUrl) or hook_failed (with error). output: Include PR URL on success or error log on failure. Set $status to committed (with prUrl) or hook_failed (with error).