openclaw-swarm-layer 0.1.0
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/LICENSE +21 -0
- package/README.md +169 -0
- package/dist/src/cli/context.d.ts +18 -0
- package/dist/src/cli/context.js +60 -0
- package/dist/src/cli/output.d.ts +1 -0
- package/dist/src/cli/output.js +9 -0
- package/dist/src/cli/register-swarm-cli.d.ts +6 -0
- package/dist/src/cli/register-swarm-cli.js +130 -0
- package/dist/src/cli/swarm-doctor.d.ts +40 -0
- package/dist/src/cli/swarm-doctor.js +69 -0
- package/dist/src/cli/swarm-init.d.ts +9 -0
- package/dist/src/cli/swarm-init.js +10 -0
- package/dist/src/cli/swarm-plan.d.ts +13 -0
- package/dist/src/cli/swarm-plan.js +39 -0
- package/dist/src/cli/swarm-report.d.ts +9 -0
- package/dist/src/cli/swarm-report.js +14 -0
- package/dist/src/cli/swarm-review.d.ts +8 -0
- package/dist/src/cli/swarm-review.js +34 -0
- package/dist/src/cli/swarm-run.d.ts +7 -0
- package/dist/src/cli/swarm-run.js +39 -0
- package/dist/src/cli/swarm-session-cancel.d.ts +6 -0
- package/dist/src/cli/swarm-session-cancel.js +64 -0
- package/dist/src/cli/swarm-session-cleanup.d.ts +16 -0
- package/dist/src/cli/swarm-session-cleanup.js +34 -0
- package/dist/src/cli/swarm-session-close.d.ts +6 -0
- package/dist/src/cli/swarm-session-close.js +53 -0
- package/dist/src/cli/swarm-session-followup.d.ts +7 -0
- package/dist/src/cli/swarm-session-followup.js +63 -0
- package/dist/src/cli/swarm-session-inspect.d.ts +5 -0
- package/dist/src/cli/swarm-session-inspect.js +12 -0
- package/dist/src/cli/swarm-session-list.d.ts +4 -0
- package/dist/src/cli/swarm-session-list.js +19 -0
- package/dist/src/cli/swarm-session-status.d.ts +5 -0
- package/dist/src/cli/swarm-session-status.js +85 -0
- package/dist/src/cli/swarm-session-steer.d.ts +6 -0
- package/dist/src/cli/swarm-session-steer.js +40 -0
- package/dist/src/cli/swarm-status.d.ts +81 -0
- package/dist/src/cli/swarm-status.js +56 -0
- package/dist/src/config.d.ts +159 -0
- package/dist/src/config.js +292 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.js +24 -0
- package/dist/src/lib/json-file.d.ts +5 -0
- package/dist/src/lib/json-file.js +42 -0
- package/dist/src/lib/paths.d.ts +25 -0
- package/dist/src/lib/paths.js +41 -0
- package/dist/src/planning/planner.d.ts +3 -0
- package/dist/src/planning/planner.js +39 -0
- package/dist/src/planning/task-graph.d.ts +8 -0
- package/dist/src/planning/task-graph.js +59 -0
- package/dist/src/reporting/obsidian-journal.d.ts +7 -0
- package/dist/src/reporting/obsidian-journal.js +126 -0
- package/dist/src/reporting/operator-summary.d.ts +32 -0
- package/dist/src/reporting/operator-summary.js +124 -0
- package/dist/src/reporting/reporter.d.ts +10 -0
- package/dist/src/reporting/reporter.js +128 -0
- package/dist/src/review/review-gate.d.ts +15 -0
- package/dist/src/review/review-gate.js +116 -0
- package/dist/src/runtime/acp-mapping.d.ts +23 -0
- package/dist/src/runtime/acp-mapping.js +50 -0
- package/dist/src/runtime/acp-runner.d.ts +11 -0
- package/dist/src/runtime/acp-runner.js +83 -0
- package/dist/src/runtime/bridge-errors.d.ts +8 -0
- package/dist/src/runtime/bridge-errors.js +59 -0
- package/dist/src/runtime/bridge-manifest.d.ts +30 -0
- package/dist/src/runtime/bridge-manifest.js +87 -0
- package/dist/src/runtime/bridge-openclaw-session-adapter.d.ts +48 -0
- package/dist/src/runtime/bridge-openclaw-session-adapter.js +142 -0
- package/dist/src/runtime/bridge-openclaw-subagent-adapter.d.ts +33 -0
- package/dist/src/runtime/bridge-openclaw-subagent-adapter.js +149 -0
- package/dist/src/runtime/manual-runner.d.ts +9 -0
- package/dist/src/runtime/manual-runner.js +53 -0
- package/dist/src/runtime/openclaw-exec-bridge.d.ts +211 -0
- package/dist/src/runtime/openclaw-exec-bridge.js +498 -0
- package/dist/src/runtime/openclaw-session-adapter.d.ts +48 -0
- package/dist/src/runtime/openclaw-session-adapter.js +14 -0
- package/dist/src/runtime/openclaw-subagent-adapter.d.ts +42 -0
- package/dist/src/runtime/openclaw-subagent-adapter.js +11 -0
- package/dist/src/runtime/public-api-seams.d.ts +23 -0
- package/dist/src/runtime/public-api-seams.js +79 -0
- package/dist/src/runtime/real-openclaw-session-adapter.d.ts +83 -0
- package/dist/src/runtime/real-openclaw-session-adapter.js +91 -0
- package/dist/src/runtime/retry-engine.d.ts +7 -0
- package/dist/src/runtime/retry-engine.js +29 -0
- package/dist/src/runtime/runner-registry.d.ts +6 -0
- package/dist/src/runtime/runner-registry.js +25 -0
- package/dist/src/runtime/session-sync.d.ts +9 -0
- package/dist/src/runtime/session-sync.js +165 -0
- package/dist/src/runtime/subagent-mapping.d.ts +9 -0
- package/dist/src/runtime/subagent-mapping.js +31 -0
- package/dist/src/runtime/subagent-runner.d.ts +9 -0
- package/dist/src/runtime/subagent-runner.js +63 -0
- package/dist/src/runtime/task-runner.d.ts +38 -0
- package/dist/src/runtime/task-runner.js +1 -0
- package/dist/src/schemas/run.schema.json +51 -0
- package/dist/src/schemas/spec.schema.json +30 -0
- package/dist/src/schemas/task.schema.json +48 -0
- package/dist/src/schemas/workflow-state.schema.json +46 -0
- package/dist/src/services/orchestrator.d.ts +47 -0
- package/dist/src/services/orchestrator.js +224 -0
- package/dist/src/session/session-lifecycle.d.ts +6 -0
- package/dist/src/session/session-lifecycle.js +84 -0
- package/dist/src/session/session-selector.d.ts +12 -0
- package/dist/src/session/session-selector.js +72 -0
- package/dist/src/session/session-store.d.ts +14 -0
- package/dist/src/session/session-store.js +84 -0
- package/dist/src/spec/spec-importer.d.ts +4 -0
- package/dist/src/spec/spec-importer.js +80 -0
- package/dist/src/state/state-store.d.ts +22 -0
- package/dist/src/state/state-store.js +187 -0
- package/dist/src/tools/index.d.ts +2 -0
- package/dist/src/tools/index.js +116 -0
- package/dist/src/types.d.ts +151 -0
- package/dist/src/types.js +1 -0
- package/dist/src/workspace/workspace-manager.d.ts +8 -0
- package/dist/src/workspace/workspace-manager.js +18 -0
- package/openclaw.plugin.json +121 -0
- package/package.json +62 -0
- package/scripts/openclaw-exec-bridge.mjs +4 -0
- package/skills/swarm-layer/SKILL.md +358 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Xu Cheng
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
# OpenClaw Swarm Layer
|
|
4
|
+
|
|
5
|
+
### Spec-Driven Workflow Orchestration for AI Agent Swarms
|
|
6
|
+
|
|
7
|
+
**The Evolution of Agent Orchestration:** Spec → Plan → Execute → Review → Ship
|
|
8
|
+
|
|
9
|
+
Turn workflow specifications into executable task graphs, dispatch through pluggable runners, and orchestrate multi-agent collaboration with persistent sessions.
|
|
10
|
+
|
|
11
|
+
[](LICENSE)
|
|
12
|
+
[](https://nodejs.org)
|
|
13
|
+
[](https://openclaw.dev)
|
|
14
|
+
[](#development)
|
|
15
|
+
|
|
16
|
+
[Quick Start](#quick-start) • [Features](#features) • [CLI Reference](#cli-commands) • [Configuration](docs/configuration.md) • [License](LICENSE)
|
|
17
|
+
|
|
18
|
+

|
|
19
|
+

|
|
20
|
+

|
|
21
|
+

|
|
22
|
+

|
|
23
|
+

|
|
24
|
+

|
|
25
|
+
|
|
26
|
+
</div>
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Features
|
|
31
|
+
|
|
32
|
+
- **Spec-driven orchestration** — import a spec, generate a task graph, execute in dependency order
|
|
33
|
+
- **Multiple runners** — manual (operator-driven), ACP (external harness), subagent (OpenClaw-native)
|
|
34
|
+
- **Review gates** — tasks require approval before marking done
|
|
35
|
+
- **Session management** — persistent sessions with reuse, thread binding, follow-up/steer
|
|
36
|
+
- **Automatic retry** — configurable retry policy with dead letter tracking
|
|
37
|
+
- **Operator UX** — attention items, highlights, recommended actions, reports
|
|
38
|
+
- **Report sync** — local reports + optional Obsidian vault sync
|
|
39
|
+
|
|
40
|
+
## Prerequisites
|
|
41
|
+
|
|
42
|
+
- Node.js >= 22
|
|
43
|
+
- [OpenClaw](https://openclaw.dev) >= 2026.2.24
|
|
44
|
+
|
|
45
|
+
## Installation
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Clone the repository
|
|
49
|
+
git clone https://github.com/xucheng/openclaw-swarm-layer.git
|
|
50
|
+
cd openclaw-swarm-layer
|
|
51
|
+
npm install
|
|
52
|
+
npm run build
|
|
53
|
+
|
|
54
|
+
# Install as an OpenClaw plugin
|
|
55
|
+
openclaw plugins install -l /path/to/openclaw-swarm-layer
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Quick Start
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
# 1. Initialize a project
|
|
62
|
+
openclaw swarm init --project /path/to/your/project
|
|
63
|
+
|
|
64
|
+
# 2. Write a spec file (Markdown with Goals/Phases/Tasks sections)
|
|
65
|
+
cat > SPEC.md << 'EOF'
|
|
66
|
+
# My Workflow
|
|
67
|
+
|
|
68
|
+
## Goals
|
|
69
|
+
- Build and test the feature
|
|
70
|
+
|
|
71
|
+
## Phases
|
|
72
|
+
### Phase 1
|
|
73
|
+
- Implement the core logic
|
|
74
|
+
- Write unit tests
|
|
75
|
+
### Phase 2
|
|
76
|
+
- Integration testing
|
|
77
|
+
EOF
|
|
78
|
+
|
|
79
|
+
# 3. Import the spec and generate task graph
|
|
80
|
+
openclaw swarm plan --project . --spec SPEC.md
|
|
81
|
+
|
|
82
|
+
# 4. Check status
|
|
83
|
+
openclaw swarm status --project .
|
|
84
|
+
|
|
85
|
+
# 5. Execute (dry-run first)
|
|
86
|
+
openclaw swarm run --project . --dry-run
|
|
87
|
+
openclaw swarm run --project .
|
|
88
|
+
|
|
89
|
+
# 6. Review completed tasks
|
|
90
|
+
openclaw swarm review --project . --task <taskId> --approve
|
|
91
|
+
|
|
92
|
+
# 7. Generate report
|
|
93
|
+
openclaw swarm report --project .
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## CLI Commands
|
|
97
|
+
|
|
98
|
+
### Core Workflow
|
|
99
|
+
| Command | Description |
|
|
100
|
+
|---------|-------------|
|
|
101
|
+
| `swarm init --project <path>` | Initialize swarm state for a project |
|
|
102
|
+
| `swarm status --project <path>` | Show workflow status with attention items |
|
|
103
|
+
| `swarm plan --project <path> --spec <path>` | Import spec and build task graph |
|
|
104
|
+
| `swarm run --project <path> [--runner acp\|manual\|subagent] [--dry-run]` | Execute next runnable task |
|
|
105
|
+
| `swarm review --project <path> --task <id> --approve\|--reject` | Approve or reject a task |
|
|
106
|
+
| `swarm report --project <path>` | Generate workflow report |
|
|
107
|
+
| `swarm doctor` | Diagnose bridge compatibility |
|
|
108
|
+
|
|
109
|
+
### Session Management
|
|
110
|
+
| Command | Description |
|
|
111
|
+
|---------|-------------|
|
|
112
|
+
| `swarm session list --project <path>` | List all sessions |
|
|
113
|
+
| `swarm session inspect --project <path> --session <id>` | Inspect a session |
|
|
114
|
+
| `swarm session status --project <path> --run <id>` | Poll session status |
|
|
115
|
+
| `swarm session cancel --project <path> --run <id>` | Cancel an active session |
|
|
116
|
+
| `swarm session close --project <path> --run <id>` | Close a session |
|
|
117
|
+
| `swarm session follow-up --project <path> --session <id> --task <desc>` | Inject follow-up task |
|
|
118
|
+
| `swarm session steer --project <path> --session <id> --message <text>` | Send steering message |
|
|
119
|
+
| `swarm session cleanup --project <path> [--stale-minutes <n>]` | Clean up orphaned sessions |
|
|
120
|
+
|
|
121
|
+
## Architecture
|
|
122
|
+
|
|
123
|
+
```
|
|
124
|
+
CLI (src/cli/) <-> Tools (src/tools/)
|
|
125
|
+
| |
|
|
126
|
+
SwarmOrchestrator (src/services/orchestrator.ts)
|
|
127
|
+
|
|
|
128
|
+
RunnerRegistry -> TaskRunner implementations
|
|
129
|
+
| | |
|
|
130
|
+
ManualRunner AcpRunner SubagentRunner
|
|
131
|
+
|
|
|
132
|
+
StateStore + SessionStore (src/state/, src/session/)
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
See [docs/configuration.md](docs/configuration.md) for all configuration options.
|
|
136
|
+
|
|
137
|
+
## Development
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
npm run build # TypeScript -> dist/
|
|
141
|
+
npm test # Unit + e2e tests
|
|
142
|
+
npm run test:unit # Unit tests only
|
|
143
|
+
npm run test:e2e # E2E tests only
|
|
144
|
+
npm run test:watch # Watch mode
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Documentation
|
|
148
|
+
|
|
149
|
+
**User Guides:**
|
|
150
|
+
- [User Manual](docs/user-manual.md) — complete usage guide from install to daily operations (Chinese)
|
|
151
|
+
- [Skills Guide](docs/skills-guide.md) — unified skill with 5 modules (setup/operate/diagnose/report/tools)
|
|
152
|
+
- [Configuration Reference](docs/configuration.md) — all config options, journal setup, directory structure
|
|
153
|
+
|
|
154
|
+
**Operations:**
|
|
155
|
+
- [Operator Runbook](docs/operator-runbook.md) — install, smoke test, upgrade, rollback
|
|
156
|
+
- [Migration Checklist](docs/migration-checklist.md) — bridge replacement strategy
|
|
157
|
+
|
|
158
|
+
**Project History:**
|
|
159
|
+
- [Roadmap](docs/roadmap.md) — M1-M3 milestone structure
|
|
160
|
+
- [Milestones](docs/milestones.md) — definition of done per milestone
|
|
161
|
+
- [Current Plan](docs/current-plan.md) — delivery status and future directions
|
|
162
|
+
|
|
163
|
+
## License
|
|
164
|
+
|
|
165
|
+
[MIT](LICENSE)
|
|
166
|
+
|
|
167
|
+
## Contributing
|
|
168
|
+
|
|
169
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { PluginRuntime } from "openclaw/plugin-sdk";
|
|
2
|
+
import type { SwarmPluginConfig } from "../config.js";
|
|
3
|
+
import { type OpenClawSessionAdapter } from "../runtime/openclaw-session-adapter.js";
|
|
4
|
+
import { type OpenClawSubagentAdapter } from "../runtime/openclaw-subagent-adapter.js";
|
|
5
|
+
import { SessionStore } from "../session/session-store.js";
|
|
6
|
+
import { StateStore } from "../state/state-store.js";
|
|
7
|
+
export type SwarmCliContext = {
|
|
8
|
+
config?: Partial<SwarmPluginConfig>;
|
|
9
|
+
stateStore?: StateStore;
|
|
10
|
+
sessionStore?: SessionStore;
|
|
11
|
+
sessionAdapter?: OpenClawSessionAdapter;
|
|
12
|
+
subagentAdapter?: OpenClawSubagentAdapter;
|
|
13
|
+
runtime?: Pick<PluginRuntime, "config" | "system">;
|
|
14
|
+
};
|
|
15
|
+
export declare function resolveStateStore(context?: SwarmCliContext): StateStore;
|
|
16
|
+
export declare function resolveSessionStore(context?: SwarmCliContext): SessionStore;
|
|
17
|
+
export declare function resolveSessionAdapter(context?: SwarmCliContext): OpenClawSessionAdapter;
|
|
18
|
+
export declare function resolveSubagentAdapter(context?: SwarmCliContext): OpenClawSubagentAdapter;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { defaultSwarmPluginConfig } from "../config.js";
|
|
2
|
+
import { createBridgeSessionAdapter } from "../runtime/bridge-openclaw-session-adapter.js";
|
|
3
|
+
import { createBridgeSubagentAdapter } from "../runtime/bridge-openclaw-subagent-adapter.js";
|
|
4
|
+
import { UnsupportedOpenClawSessionAdapter } from "../runtime/openclaw-session-adapter.js";
|
|
5
|
+
import { UnsupportedOpenClawSubagentAdapter } from "../runtime/openclaw-subagent-adapter.js";
|
|
6
|
+
import { createSessionAdapter } from "../runtime/real-openclaw-session-adapter.js";
|
|
7
|
+
import { SessionStore } from "../session/session-store.js";
|
|
8
|
+
import { StateStore } from "../state/state-store.js";
|
|
9
|
+
export function resolveStateStore(context) {
|
|
10
|
+
return context?.stateStore ?? new StateStore(context?.config);
|
|
11
|
+
}
|
|
12
|
+
export function resolveSessionStore(context) {
|
|
13
|
+
return context?.sessionStore ?? new SessionStore(context?.stateStore?.config ?? context?.config);
|
|
14
|
+
}
|
|
15
|
+
export function resolveSessionAdapter(context) {
|
|
16
|
+
if (context?.sessionAdapter) {
|
|
17
|
+
return context.sessionAdapter;
|
|
18
|
+
}
|
|
19
|
+
const config = context?.stateStore?.config
|
|
20
|
+
? context.stateStore.config
|
|
21
|
+
: context?.config
|
|
22
|
+
? {
|
|
23
|
+
...defaultSwarmPluginConfig,
|
|
24
|
+
...context.config,
|
|
25
|
+
acp: {
|
|
26
|
+
...defaultSwarmPluginConfig.acp,
|
|
27
|
+
...context.config.acp,
|
|
28
|
+
},
|
|
29
|
+
}
|
|
30
|
+
: defaultSwarmPluginConfig;
|
|
31
|
+
const bridgeAdapter = createBridgeSessionAdapter(context?.runtime, { acp: config.acp, bridge: config.bridge });
|
|
32
|
+
if (bridgeAdapter) {
|
|
33
|
+
return bridgeAdapter;
|
|
34
|
+
}
|
|
35
|
+
const runtimeAdapter = createSessionAdapter(context?.runtime, { acp: config.acp });
|
|
36
|
+
return runtimeAdapter ?? new UnsupportedOpenClawSessionAdapter();
|
|
37
|
+
}
|
|
38
|
+
export function resolveSubagentAdapter(context) {
|
|
39
|
+
if (context?.subagentAdapter) {
|
|
40
|
+
return context.subagentAdapter;
|
|
41
|
+
}
|
|
42
|
+
const config = context?.stateStore?.config
|
|
43
|
+
? context.stateStore.config
|
|
44
|
+
: context?.config
|
|
45
|
+
? {
|
|
46
|
+
...defaultSwarmPluginConfig,
|
|
47
|
+
...context.config,
|
|
48
|
+
acp: {
|
|
49
|
+
...defaultSwarmPluginConfig.acp,
|
|
50
|
+
...context.config.acp,
|
|
51
|
+
},
|
|
52
|
+
bridge: {
|
|
53
|
+
...defaultSwarmPluginConfig.bridge,
|
|
54
|
+
...context.config.bridge,
|
|
55
|
+
},
|
|
56
|
+
}
|
|
57
|
+
: defaultSwarmPluginConfig;
|
|
58
|
+
const bridgeAdapter = createBridgeSubagentAdapter({ bridge: config.bridge });
|
|
59
|
+
return bridgeAdapter ?? new UnsupportedOpenClawSubagentAdapter();
|
|
60
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function formatOutput(payload: unknown, asJson?: boolean): string;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
+
import { type SwarmCliContext } from "./context.js";
|
|
3
|
+
export declare function registerSwarmCli(api: OpenClawPluginApi): void;
|
|
4
|
+
export declare function registerSwarmCliCommands(ctx: {
|
|
5
|
+
program: any;
|
|
6
|
+
}, cliContext?: SwarmCliContext): void;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { resolvePluginConfigFromApi } from "../config.js";
|
|
2
|
+
import { StateStore } from "../state/state-store.js";
|
|
3
|
+
import { runSwarmInit } from "./swarm-init.js";
|
|
4
|
+
import { formatOutput } from "./output.js";
|
|
5
|
+
import { runSwarmPlan } from "./swarm-plan.js";
|
|
6
|
+
import { runSwarmDoctor } from "./swarm-doctor.js";
|
|
7
|
+
import { runSwarmReport } from "./swarm-report.js";
|
|
8
|
+
import { runSwarmReview } from "./swarm-review.js";
|
|
9
|
+
import { runSwarmSessionCancel } from "./swarm-session-cancel.js";
|
|
10
|
+
import { runSwarmSessionCleanup } from "./swarm-session-cleanup.js";
|
|
11
|
+
import { runSwarmSessionClose } from "./swarm-session-close.js";
|
|
12
|
+
import { runSwarmSessionFollowup } from "./swarm-session-followup.js";
|
|
13
|
+
import { runSwarmSessionInspect } from "./swarm-session-inspect.js";
|
|
14
|
+
import { runSwarmSessionList } from "./swarm-session-list.js";
|
|
15
|
+
import { runSwarmRun } from "./swarm-run.js";
|
|
16
|
+
import { runSwarmSessionStatus } from "./swarm-session-status.js";
|
|
17
|
+
import { runSwarmSessionSteer } from "./swarm-session-steer.js";
|
|
18
|
+
import { runSwarmStatus } from "./swarm-status.js";
|
|
19
|
+
function bindCommand(command, action) {
|
|
20
|
+
command.action(async (options) => {
|
|
21
|
+
const output = await action(options);
|
|
22
|
+
if (output !== undefined) {
|
|
23
|
+
process.stdout.write(`${formatOutput(output, Boolean(options?.json))}\n`);
|
|
24
|
+
}
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
export function registerSwarmCli(api) {
|
|
28
|
+
api.registerCli((ctx) => {
|
|
29
|
+
registerSwarmCliCommands(ctx, {
|
|
30
|
+
config: resolvePluginConfigFromApi(api),
|
|
31
|
+
stateStore: new StateStore(resolvePluginConfigFromApi(api)),
|
|
32
|
+
runtime: api.runtime,
|
|
33
|
+
});
|
|
34
|
+
}, { commands: ["swarm"] });
|
|
35
|
+
}
|
|
36
|
+
export function registerSwarmCliCommands(ctx, cliContext) {
|
|
37
|
+
const swarm = ctx.program.command("swarm").description("Swarm workflow commands");
|
|
38
|
+
const init = swarm.command("init").requiredOption("--project <path>").option("--json");
|
|
39
|
+
bindCommand(init, (options) => runSwarmInit({ project: options.project }, cliContext));
|
|
40
|
+
const status = swarm.command("status").requiredOption("--project <path>").option("--json");
|
|
41
|
+
bindCommand(status, (options) => runSwarmStatus({ project: options.project }, cliContext));
|
|
42
|
+
const plan = swarm
|
|
43
|
+
.command("plan")
|
|
44
|
+
.requiredOption("--project <path>")
|
|
45
|
+
.requiredOption("--spec <path>")
|
|
46
|
+
.option("--json");
|
|
47
|
+
bindCommand(plan, (options) => runSwarmPlan({ project: options.project, spec: options.spec }, cliContext));
|
|
48
|
+
const run = swarm
|
|
49
|
+
.command("run")
|
|
50
|
+
.requiredOption("--project <path>")
|
|
51
|
+
.option("--task <taskId>")
|
|
52
|
+
.option("--dry-run")
|
|
53
|
+
.option("--runner <kind>")
|
|
54
|
+
.option("--json");
|
|
55
|
+
bindCommand(run, (options) => runSwarmRun({
|
|
56
|
+
project: options.project,
|
|
57
|
+
task: options.task,
|
|
58
|
+
dryRun: options.dryRun,
|
|
59
|
+
runner: options.runner,
|
|
60
|
+
}, cliContext));
|
|
61
|
+
const review = swarm
|
|
62
|
+
.command("review")
|
|
63
|
+
.requiredOption("--project <path>")
|
|
64
|
+
.requiredOption("--task <taskId>")
|
|
65
|
+
.option("--approve")
|
|
66
|
+
.option("--reject")
|
|
67
|
+
.option("--note <text>")
|
|
68
|
+
.option("--json");
|
|
69
|
+
bindCommand(review, (options) => runSwarmReview({
|
|
70
|
+
project: options.project,
|
|
71
|
+
task: options.task,
|
|
72
|
+
approve: options.approve,
|
|
73
|
+
reject: options.reject,
|
|
74
|
+
note: options.note,
|
|
75
|
+
}, cliContext));
|
|
76
|
+
const report = swarm.command("report").requiredOption("--project <path>").option("--json");
|
|
77
|
+
bindCommand(report, (options) => runSwarmReport({ project: options.project }, cliContext));
|
|
78
|
+
const doctor = swarm.command("doctor").option("--json");
|
|
79
|
+
bindCommand(doctor, () => runSwarmDoctor({}, cliContext));
|
|
80
|
+
const session = swarm.command("session").description("ACP session operator commands");
|
|
81
|
+
const sessionList = session.command("list").requiredOption("--project <path>").option("--json");
|
|
82
|
+
bindCommand(sessionList, (options) => runSwarmSessionList({ project: options.project }, cliContext));
|
|
83
|
+
const sessionInspect = session
|
|
84
|
+
.command("inspect")
|
|
85
|
+
.requiredOption("--project <path>")
|
|
86
|
+
.requiredOption("--session <sessionId>")
|
|
87
|
+
.option("--json");
|
|
88
|
+
bindCommand(sessionInspect, (options) => runSwarmSessionInspect({ project: options.project, session: options.session }, cliContext));
|
|
89
|
+
const sessionStatus = session
|
|
90
|
+
.command("status")
|
|
91
|
+
.requiredOption("--project <path>")
|
|
92
|
+
.requiredOption("--run <runId>")
|
|
93
|
+
.option("--json");
|
|
94
|
+
bindCommand(sessionStatus, (options) => runSwarmSessionStatus({ project: options.project, run: options.run }, cliContext));
|
|
95
|
+
const sessionCancel = session
|
|
96
|
+
.command("cancel")
|
|
97
|
+
.requiredOption("--project <path>")
|
|
98
|
+
.requiredOption("--run <runId>")
|
|
99
|
+
.option("--reason <text>")
|
|
100
|
+
.option("--json");
|
|
101
|
+
bindCommand(sessionCancel, (options) => runSwarmSessionCancel({ project: options.project, run: options.run, reason: options.reason }, cliContext));
|
|
102
|
+
const sessionClose = session
|
|
103
|
+
.command("close")
|
|
104
|
+
.requiredOption("--project <path>")
|
|
105
|
+
.requiredOption("--run <runId>")
|
|
106
|
+
.option("--reason <text>")
|
|
107
|
+
.option("--json");
|
|
108
|
+
bindCommand(sessionClose, (options) => runSwarmSessionClose({ project: options.project, run: options.run, reason: options.reason }, cliContext));
|
|
109
|
+
const sessionFollowup = session
|
|
110
|
+
.command("follow-up")
|
|
111
|
+
.requiredOption("--project <path>")
|
|
112
|
+
.requiredOption("--session <sessionId>")
|
|
113
|
+
.requiredOption("--task <description>")
|
|
114
|
+
.option("--runner <kind>")
|
|
115
|
+
.option("--json");
|
|
116
|
+
bindCommand(sessionFollowup, (options) => runSwarmSessionFollowup({ project: options.project, session: options.session, task: options.task, runner: options.runner }, cliContext));
|
|
117
|
+
const sessionCleanup = session
|
|
118
|
+
.command("cleanup")
|
|
119
|
+
.requiredOption("--project <path>")
|
|
120
|
+
.option("--stale-minutes <minutes>")
|
|
121
|
+
.option("--json");
|
|
122
|
+
bindCommand(sessionCleanup, (options) => runSwarmSessionCleanup({ project: options.project, staleMinutes: options.staleMinutes ? Number(options.staleMinutes) : undefined }, cliContext));
|
|
123
|
+
const sessionSteer = session
|
|
124
|
+
.command("steer")
|
|
125
|
+
.requiredOption("--project <path>")
|
|
126
|
+
.requiredOption("--session <sessionId>")
|
|
127
|
+
.requiredOption("--message <text>")
|
|
128
|
+
.option("--json");
|
|
129
|
+
bindCommand(sessionSteer, (options) => runSwarmSessionSteer({ project: options.project, session: options.session, message: options.message }, cliContext));
|
|
130
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { runBridgeCommandDirect } from "../runtime/bridge-openclaw-session-adapter.js";
|
|
2
|
+
import { type SwarmCliContext } from "./context.js";
|
|
3
|
+
type BridgeDoctorResult = {
|
|
4
|
+
ok: boolean;
|
|
5
|
+
severity: "healthy" | "warning" | "blocked";
|
|
6
|
+
openclawRoot: string;
|
|
7
|
+
version?: string;
|
|
8
|
+
compatibility: {
|
|
9
|
+
strategy?: "internal-bundle";
|
|
10
|
+
testedAt?: string;
|
|
11
|
+
supportedRunners: string[];
|
|
12
|
+
replacementCandidates: string[];
|
|
13
|
+
notes: string[];
|
|
14
|
+
};
|
|
15
|
+
publicApi: {
|
|
16
|
+
acpControlPlaneExport: boolean;
|
|
17
|
+
subagentSpawnExport: boolean;
|
|
18
|
+
readyReplacementPoints: string[];
|
|
19
|
+
};
|
|
20
|
+
replacementPlan: Array<{
|
|
21
|
+
runner: "acp" | "subagent";
|
|
22
|
+
publicExport: string;
|
|
23
|
+
available: boolean;
|
|
24
|
+
status: "ready" | "blocked";
|
|
25
|
+
currentImplementation: string;
|
|
26
|
+
targetImplementation: string;
|
|
27
|
+
affectedModules: string[];
|
|
28
|
+
nextStep: string;
|
|
29
|
+
}>;
|
|
30
|
+
migrationChecklist: string[];
|
|
31
|
+
checks: Record<string, boolean>;
|
|
32
|
+
blockers: string[];
|
|
33
|
+
warnings: string[];
|
|
34
|
+
risks: string[];
|
|
35
|
+
remediation: string[];
|
|
36
|
+
nextAction: string;
|
|
37
|
+
};
|
|
38
|
+
export type BridgeCommandRunner = typeof runBridgeCommandDirect;
|
|
39
|
+
export declare function runSwarmDoctor(_options: Record<string, never>, context?: SwarmCliContext, commandRunner?: BridgeCommandRunner): Promise<BridgeDoctorResult>;
|
|
40
|
+
export {};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { defaultSwarmPluginConfig } from "../config.js";
|
|
2
|
+
import { resolveBridgeScriptPath, resolveTsxLoaderPath, runBridgeCommandDirect } from "../runtime/bridge-openclaw-session-adapter.js";
|
|
3
|
+
function resolveEffectiveConfig(context) {
|
|
4
|
+
if (context?.stateStore?.config) {
|
|
5
|
+
return context.stateStore.config;
|
|
6
|
+
}
|
|
7
|
+
if (context?.config) {
|
|
8
|
+
return {
|
|
9
|
+
...defaultSwarmPluginConfig,
|
|
10
|
+
...context.config,
|
|
11
|
+
acp: {
|
|
12
|
+
...defaultSwarmPluginConfig.acp,
|
|
13
|
+
...context.config.acp,
|
|
14
|
+
},
|
|
15
|
+
bridge: {
|
|
16
|
+
...defaultSwarmPluginConfig.bridge,
|
|
17
|
+
...context.config.bridge,
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
return defaultSwarmPluginConfig;
|
|
22
|
+
}
|
|
23
|
+
export async function runSwarmDoctor(_options, context, commandRunner = runBridgeCommandDirect) {
|
|
24
|
+
const config = resolveEffectiveConfig(context);
|
|
25
|
+
if (!config.bridge.enabled) {
|
|
26
|
+
return {
|
|
27
|
+
ok: false,
|
|
28
|
+
severity: "blocked",
|
|
29
|
+
openclawRoot: config.bridge.openclawRoot ?? "(unset)",
|
|
30
|
+
compatibility: {
|
|
31
|
+
supportedRunners: [],
|
|
32
|
+
replacementCandidates: [],
|
|
33
|
+
notes: [],
|
|
34
|
+
},
|
|
35
|
+
publicApi: {
|
|
36
|
+
acpControlPlaneExport: false,
|
|
37
|
+
subagentSpawnExport: false,
|
|
38
|
+
readyReplacementPoints: [],
|
|
39
|
+
},
|
|
40
|
+
replacementPlan: [],
|
|
41
|
+
migrationChecklist: [
|
|
42
|
+
"Enable bridge mode first, then rerun `openclaw swarm doctor --json` to generate a migration checklist.",
|
|
43
|
+
],
|
|
44
|
+
checks: {
|
|
45
|
+
bridgeEnabled: false,
|
|
46
|
+
},
|
|
47
|
+
blockers: ["bridge.enabled=false"],
|
|
48
|
+
warnings: [],
|
|
49
|
+
risks: ["bridge mode is disabled"],
|
|
50
|
+
remediation: ["Enable plugins.entries.openclaw-swarm-layer.config.bridge.enabled before using bridge-backed execution."],
|
|
51
|
+
nextAction: "Enable bridge mode before running bridge-backed ACP or subagent execution.",
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const result = await commandRunner([config.bridge.nodePath ?? process.execPath, "--import", resolveTsxLoaderPath(), resolveBridgeScriptPath(), "doctor"], {
|
|
55
|
+
timeoutMs: 120_000,
|
|
56
|
+
cwd: process.cwd(),
|
|
57
|
+
input: JSON.stringify({
|
|
58
|
+
bridge: {
|
|
59
|
+
openclawRoot: config.bridge.openclawRoot,
|
|
60
|
+
versionAllow: config.bridge.versionAllow,
|
|
61
|
+
},
|
|
62
|
+
}),
|
|
63
|
+
});
|
|
64
|
+
if (result.code !== 0) {
|
|
65
|
+
throw new Error(result.stderr.trim() || result.stdout.trim() || "bridge doctor failed");
|
|
66
|
+
}
|
|
67
|
+
const parsed = JSON.parse(result.stdout);
|
|
68
|
+
return parsed.result;
|
|
69
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type SwarmCliContext } from "./context.js";
|
|
2
|
+
export type SwarmInitResult = {
|
|
3
|
+
ok: true;
|
|
4
|
+
projectRoot: string;
|
|
5
|
+
swarmRoot: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function runSwarmInit(options: {
|
|
8
|
+
project: string;
|
|
9
|
+
}, context?: SwarmCliContext): Promise<SwarmInitResult>;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { resolveStateStore } from "./context.js";
|
|
2
|
+
export async function runSwarmInit(options, context) {
|
|
3
|
+
const stateStore = resolveStateStore(context);
|
|
4
|
+
const paths = await stateStore.initProject(options.project);
|
|
5
|
+
return {
|
|
6
|
+
ok: true,
|
|
7
|
+
projectRoot: paths.projectRoot,
|
|
8
|
+
swarmRoot: paths.swarmRoot,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type SwarmCliContext } from "./context.js";
|
|
2
|
+
export type SwarmPlanResult = {
|
|
3
|
+
ok: true;
|
|
4
|
+
specId: string;
|
|
5
|
+
taskCount: number;
|
|
6
|
+
activeSpecId: string;
|
|
7
|
+
localReportPath: string;
|
|
8
|
+
obsidianReportPath?: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function runSwarmPlan(options: {
|
|
11
|
+
project: string;
|
|
12
|
+
spec: string;
|
|
13
|
+
}, context?: SwarmCliContext): Promise<SwarmPlanResult>;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { resolveSwarmPaths } from "../lib/paths.js";
|
|
2
|
+
import { planTasksFromSpec } from "../planning/planner.js";
|
|
3
|
+
import { journalSpecArchive } from "../reporting/obsidian-journal.js";
|
|
4
|
+
import { writeWorkflowReport } from "../reporting/reporter.js";
|
|
5
|
+
import { importSpecFromMarkdown } from "../spec/spec-importer.js";
|
|
6
|
+
import { resolveStateStore } from "./context.js";
|
|
7
|
+
export async function runSwarmPlan(options, context) {
|
|
8
|
+
const stateStore = resolveStateStore(context);
|
|
9
|
+
const reportConfig = context?.config ?? stateStore.config;
|
|
10
|
+
const workflow = await stateStore.loadWorkflow(options.project);
|
|
11
|
+
const spec = await importSpecFromMarkdown(options.spec, { defaultProjectRoot: workflow.projectRoot });
|
|
12
|
+
const tasks = planTasksFromSpec(spec, context?.config);
|
|
13
|
+
await stateStore.writeSpec(options.project, spec);
|
|
14
|
+
const nextWorkflow = {
|
|
15
|
+
...workflow,
|
|
16
|
+
activeSpecId: spec.specId,
|
|
17
|
+
lifecycle: tasks.length > 0 ? "planned" : workflow.lifecycle,
|
|
18
|
+
tasks,
|
|
19
|
+
reviewQueue: [],
|
|
20
|
+
lastAction: {
|
|
21
|
+
at: new Date().toISOString(),
|
|
22
|
+
type: "plan",
|
|
23
|
+
message: `planned ${tasks.length} tasks for ${spec.specId}`,
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
await stateStore.saveWorkflow(options.project, nextWorkflow);
|
|
27
|
+
const report = await writeWorkflowReport(options.project, nextWorkflow, reportConfig);
|
|
28
|
+
// Obsidian journal: spec archive
|
|
29
|
+
const paths = resolveSwarmPaths(options.project, reportConfig);
|
|
30
|
+
await journalSpecArchive(paths, stateStore.config.obsidianJournal, spec);
|
|
31
|
+
return {
|
|
32
|
+
ok: true,
|
|
33
|
+
specId: spec.specId,
|
|
34
|
+
taskCount: tasks.length,
|
|
35
|
+
activeSpecId: spec.specId,
|
|
36
|
+
localReportPath: report.localReportPath,
|
|
37
|
+
obsidianReportPath: report.obsidianReportPath,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { writeWorkflowReport } from "../reporting/reporter.js";
|
|
2
|
+
import { resolveStateStore } from "./context.js";
|
|
3
|
+
export async function runSwarmReport(options, context) {
|
|
4
|
+
const stateStore = resolveStateStore(context);
|
|
5
|
+
const reportConfig = context?.config ?? stateStore.config;
|
|
6
|
+
const workflow = await stateStore.loadWorkflow(options.project);
|
|
7
|
+
const written = await writeWorkflowReport(options.project, workflow, reportConfig);
|
|
8
|
+
return {
|
|
9
|
+
ok: true,
|
|
10
|
+
report: written.report,
|
|
11
|
+
localReportPath: written.localReportPath,
|
|
12
|
+
obsidianReportPath: written.obsidianReportPath,
|
|
13
|
+
};
|
|
14
|
+
}
|