@tingrudeng/worker-review-orchestrator-cli 0.1.0-beta.1 → 0.1.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -1
- package/dist/cli.js +4 -0
- package/dist/dispatch.d.ts +1 -0
- package/dist/dispatch.js +57 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -25,11 +25,13 @@ This package is meant to pair with the `worker-review-orchestrator` skill, but i
|
|
|
25
25
|
- `dispatch`
|
|
26
26
|
- POST a dispatch payload to `/api/dispatches`
|
|
27
27
|
- Supports `--target-worker-id` to pin all tasks in the payload to one worker
|
|
28
|
+
- Supports `--require-existing-worker` to verify the target worker or worker pool is already online in `/api/dashboard/snapshot` before posting
|
|
28
29
|
- `dispatch-task`
|
|
29
30
|
- Build and POST a single-task dispatch payload from CLI flags
|
|
30
31
|
- Preferred when the control layer wants to send one focused task without hand-writing `dispatch.json`
|
|
31
32
|
- Supports `--worker-prompt` and `--context-markdown` for inline string inputs
|
|
32
|
-
-
|
|
33
|
+
- Supports `--worker-prompt-file` and `--context-markdown-file` for file-backed prompt/context inputs
|
|
34
|
+
- Supports `--require-existing-worker` to reject dispatch when no matching online worker exists
|
|
33
35
|
- `watch`
|
|
34
36
|
- Poll `/api/dashboard/snapshot` until a task reaches `review`, `failed`, `merged`, or `blocked`
|
|
35
37
|
- `decide`
|
|
@@ -52,6 +54,7 @@ This package is meant to pair with the `worker-review-orchestrator` skill, but i
|
|
|
52
54
|
```bash
|
|
53
55
|
forgeflow-review-orchestrator dispatch --dispatcher-url http://127.0.0.1:8787 --input dispatch.json
|
|
54
56
|
forgeflow-review-orchestrator dispatch --dispatcher-url http://127.0.0.1:8787 --input dispatch.json --target-worker-id trae-remote-forgeflow
|
|
57
|
+
forgeflow-review-orchestrator dispatch --dispatcher-url http://127.0.0.1:8787 --input dispatch.json --target-worker-id trae-local-forgeflow --require-existing-worker
|
|
55
58
|
forgeflow-review-orchestrator dispatch-task --dispatcher-url http://127.0.0.1:8787 --repo TingRuDeng/ForgeFlow --default-branch main --task-id task-1 --title "Update docs" --pool trae --branch-name ai/trae/task-1 --allowed-paths docs/**,README.md --acceptance "pnpm typecheck,git diff --check" --target-worker-id trae-remote-forgeflow
|
|
56
59
|
forgeflow-review-orchestrator dispatch-task --dispatcher-url http://127.0.0.1:8787 --repo TingRuDeng/ForgeFlow --default-branch main --task-id task-1 --title "Refactor auth" --pool codex --branch-name ai/codex/auth --allowed-paths "packages/auth/**" --acceptance "pnpm typecheck" --worker-prompt "You are a codex worker. Refactor the auth module." --context-markdown "# Context\n\nFocus on packages/auth only."
|
|
57
60
|
forgeflow-review-orchestrator watch --dispatcher-url http://127.0.0.1:8787 --task-id dispatch-1:task-1
|
|
@@ -63,6 +66,8 @@ forgeflow-review-orchestrator inspect --dispatcher-url http://127.0.0.1:8787 --t
|
|
|
63
66
|
|
|
64
67
|
When `--target-worker-id` is set, the CLI injects that worker id into every task and assignment package before posting the dispatch. This keeps the existing `dispatch.json` shape but gives the control layer an explicit way to target `trae-local-forgeflow` or `trae-remote-forgeflow`.
|
|
65
68
|
|
|
69
|
+
When `--require-existing-worker` is set, the CLI fetches `/api/dashboard/snapshot` before dispatch and hard-fails if the pinned target worker is offline or if no online worker exists for the required task pool. Use this when the control layer must reuse an already running dispatcher + worker runtime instead of implicitly relying on a later bootstrap step.
|
|
70
|
+
|
|
66
71
|
Use `dispatch-task` for the common case of one worker task with one branch, one `allowedPaths` list, and one `acceptance` list. Keep `dispatch --input` for more complex multi-task or prebuilt payloads.
|
|
67
72
|
|
|
68
73
|
For control-layer review flow, prefer this CLI over calling lower-level review-decision scripts directly. Keep the scripts as compatibility and implementation detail, but use `forgeflow-review-orchestrator decide` as the default operator entrypoint.
|
package/dist/cli.js
CHANGED
|
@@ -51,7 +51,9 @@ function printHelp() {
|
|
|
51
51
|
Usage:
|
|
52
52
|
forgeflow-review-orchestrator dispatch --dispatcher-url http://127.0.0.1:8787 --input dispatch.json
|
|
53
53
|
forgeflow-review-orchestrator dispatch --dispatcher-url http://127.0.0.1:8787 --input dispatch.json --target-worker-id trae-remote-forgeflow
|
|
54
|
+
forgeflow-review-orchestrator dispatch --dispatcher-url http://127.0.0.1:8787 --input dispatch.json --require-existing-worker
|
|
54
55
|
forgeflow-review-orchestrator dispatch-task --dispatcher-url http://127.0.0.1:8787 --repo TingRuDeng/ForgeFlow --default-branch main --task-id task-1 --title "Update docs" --pool trae --branch-name ai/trae/task-1 --allowed-paths docs/**,README.md --acceptance "pnpm typecheck,git diff --check"
|
|
56
|
+
forgeflow-review-orchestrator dispatch-task --dispatcher-url http://127.0.0.1:8787 --repo TingRuDeng/ForgeFlow --default-branch main --task-id task-1 --title "Update docs" --pool trae --branch-name ai/trae/task-1 --target-worker-id trae-local-forgeflow --require-existing-worker
|
|
55
57
|
forgeflow-review-orchestrator dispatch-task --dispatcher-url http://127.0.0.1:8787 --repo TingRuDeng/ForgeFlow --default-branch main --task-id task-1 --title "Update docs" --pool trae --branch-name ai/trae/task-1 --worker-prompt-file prompts/worker.md --context-markdown-file context/task.md
|
|
56
58
|
forgeflow-review-orchestrator watch --dispatcher-url http://127.0.0.1:8787 --task-id dispatch-1:task-1
|
|
57
59
|
forgeflow-review-orchestrator watch --dispatcher-url http://127.0.0.1:8787 --task-id dispatch-1:task-1 --summary
|
|
@@ -89,6 +91,7 @@ export async function runCli(argv, partialDeps = {}) {
|
|
|
89
91
|
const result = await deps.runDispatch({
|
|
90
92
|
dispatcherUrl,
|
|
91
93
|
input,
|
|
94
|
+
requireExistingWorker: options.requireExistingWorker === true,
|
|
92
95
|
targetWorkerId: typeof options.targetWorkerId === "string" ? options.targetWorkerId : undefined,
|
|
93
96
|
requestTimeoutMs: typeof options.requestTimeoutMs === "number" ? options.requestTimeoutMs : undefined,
|
|
94
97
|
});
|
|
@@ -136,6 +139,7 @@ export async function runCli(argv, partialDeps = {}) {
|
|
|
136
139
|
dispatcherUrl,
|
|
137
140
|
input: "-",
|
|
138
141
|
payload,
|
|
142
|
+
requireExistingWorker: options.requireExistingWorker === true,
|
|
139
143
|
requestTimeoutMs: typeof options.requestTimeoutMs === "number" ? options.requestTimeoutMs : undefined,
|
|
140
144
|
});
|
|
141
145
|
deps.log(JSON.stringify(result, null, 2));
|
package/dist/dispatch.d.ts
CHANGED
package/dist/dispatch.js
CHANGED
|
@@ -90,10 +90,67 @@ export function applyDispatchTargetWorker(payload, targetWorkerId) {
|
|
|
90
90
|
packages: applyTargetWorkerToRecords(payload.packages, normalizedTargetWorkerId),
|
|
91
91
|
};
|
|
92
92
|
}
|
|
93
|
+
function normalizeString(value) {
|
|
94
|
+
if (typeof value !== "string") {
|
|
95
|
+
return undefined;
|
|
96
|
+
}
|
|
97
|
+
const trimmed = value.trim();
|
|
98
|
+
return trimmed ? trimmed : undefined;
|
|
99
|
+
}
|
|
100
|
+
function isOnlineWorkerStatus(status) {
|
|
101
|
+
return status === "idle" || status === "busy";
|
|
102
|
+
}
|
|
103
|
+
function formatWorkerInventory(workers) {
|
|
104
|
+
if (workers.length === 0) {
|
|
105
|
+
return "none";
|
|
106
|
+
}
|
|
107
|
+
return workers.map((worker) => {
|
|
108
|
+
const id = normalizeString(worker.id) || "<unknown>";
|
|
109
|
+
const pool = normalizeString(worker.pool) || "<unknown-pool>";
|
|
110
|
+
const status = normalizeString(worker.status) || "<unknown-status>";
|
|
111
|
+
return `${id}(${pool},${status})`;
|
|
112
|
+
}).join(", ");
|
|
113
|
+
}
|
|
114
|
+
async function ensureExistingWorkersAvailable(payload, options) {
|
|
115
|
+
const client = createJsonHttpClient(options.dispatcherUrl, {
|
|
116
|
+
fetchImpl: options.fetchImpl,
|
|
117
|
+
requestTimeoutMs: options.requestTimeoutMs,
|
|
118
|
+
});
|
|
119
|
+
const snapshot = await client.request("/api/dashboard/snapshot");
|
|
120
|
+
const workers = Array.isArray(snapshot.workers) ? snapshot.workers : [];
|
|
121
|
+
const onlineWorkers = workers.filter((worker) => isOnlineWorkerStatus(worker.status));
|
|
122
|
+
if (onlineWorkers.length === 0) {
|
|
123
|
+
throw new Error(`require-existing-worker check failed: dispatcher snapshot has no online workers. Snapshot workers: ${formatWorkerInventory(workers)}`);
|
|
124
|
+
}
|
|
125
|
+
const tasks = Array.isArray(payload.tasks) ? payload.tasks : [];
|
|
126
|
+
const targetWorkerIds = [...new Set(tasks.map((task) => normalizeString(task.targetWorkerId) || normalizeString(task.target_worker_id)).filter((value) => Boolean(value)))];
|
|
127
|
+
if (targetWorkerIds.length > 0) {
|
|
128
|
+
for (const targetWorkerId of targetWorkerIds) {
|
|
129
|
+
const matchedWorker = onlineWorkers.find((worker) => normalizeString(worker.id) === targetWorkerId);
|
|
130
|
+
if (!matchedWorker) {
|
|
131
|
+
throw new Error(`require-existing-worker check failed: target worker "${targetWorkerId}" is not online. Online workers: ${formatWorkerInventory(onlineWorkers)}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const requiredPools = [...new Set(tasks.map((task) => normalizeString(task.pool)).filter((value) => Boolean(value)))];
|
|
137
|
+
if (requiredPools.length === 0) {
|
|
138
|
+
throw new Error("require-existing-worker check failed: every dispatch task must declare a pool or targetWorkerId");
|
|
139
|
+
}
|
|
140
|
+
for (const pool of requiredPools) {
|
|
141
|
+
const hasOnlineWorker = onlineWorkers.some((worker) => normalizeString(worker.pool) === pool);
|
|
142
|
+
if (!hasOnlineWorker) {
|
|
143
|
+
throw new Error(`require-existing-worker check failed: no online worker available for pool "${pool}". Online workers: ${formatWorkerInventory(onlineWorkers)}`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
93
147
|
export async function runDispatch(options) {
|
|
94
148
|
const payload = options.payload
|
|
95
149
|
? applyDispatchTargetWorker(options.payload, options.targetWorkerId)
|
|
96
150
|
: applyDispatchTargetWorker(await loadDispatchInput(options.input, options.readStdin), options.targetWorkerId);
|
|
151
|
+
if (options.requireExistingWorker) {
|
|
152
|
+
await ensureExistingWorkersAvailable(payload, options);
|
|
153
|
+
}
|
|
97
154
|
const client = createJsonHttpClient(options.dispatcherUrl, {
|
|
98
155
|
fetchImpl: options.fetchImpl,
|
|
99
156
|
requestTimeoutMs: options.requestTimeoutMs,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tingrudeng/worker-review-orchestrator-cli",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"main": "./dist/index.js",
|
|
18
18
|
"types": "./dist/index.d.ts",
|
|
19
19
|
"bin": {
|
|
20
|
-
"forgeflow-review-orchestrator": "
|
|
20
|
+
"forgeflow-review-orchestrator": "dist/cli.js"
|
|
21
21
|
},
|
|
22
22
|
"exports": {
|
|
23
23
|
".": {
|