@intrect/openswarm 0.2.1 → 0.3.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/README.md +236 -331
- package/config.example.yaml +36 -13
- package/dist/adapters/base.d.ts.map +1 -1
- package/dist/adapters/base.js +4 -0
- package/dist/adapters/base.js.map +1 -1
- package/dist/adapters/cryptoQuantAdapter.js +1 -1
- package/dist/adapters/cryptoQuantAdapter.js.map +1 -1
- package/dist/adapters/gpt.d.ts +15 -0
- package/dist/adapters/gpt.d.ts.map +1 -0
- package/dist/adapters/gpt.js +274 -0
- package/dist/adapters/gpt.js.map +1 -0
- package/dist/adapters/index.d.ts +2 -0
- package/dist/adapters/index.d.ts.map +1 -1
- package/dist/adapters/index.js +6 -0
- package/dist/adapters/index.js.map +1 -1
- package/dist/adapters/local.d.ts +22 -0
- package/dist/adapters/local.d.ts.map +1 -0
- package/dist/adapters/local.js +277 -0
- package/dist/adapters/local.js.map +1 -0
- package/dist/adapters/types.d.ts +6 -1
- package/dist/adapters/types.d.ts.map +1 -1
- package/dist/agents/pairPipeline.d.ts.map +1 -1
- package/dist/agents/pairPipeline.js +21 -6
- package/dist/agents/pairPipeline.js.map +1 -1
- package/dist/agents/pipelineGuards.d.ts.map +1 -1
- package/dist/agents/pipelineGuards.js +84 -2
- package/dist/agents/pipelineGuards.js.map +1 -1
- package/dist/auth/index.d.ts +3 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +6 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/auth/oauthPkce.d.ts +21 -0
- package/dist/auth/oauthPkce.d.ts.map +1 -0
- package/dist/auth/oauthPkce.js +212 -0
- package/dist/auth/oauthPkce.js.map +1 -0
- package/dist/auth/oauthStore.d.ts +24 -0
- package/dist/auth/oauthStore.d.ts.map +1 -0
- package/dist/auth/oauthStore.js +96 -0
- package/dist/auth/oauthStore.js.map +1 -0
- package/dist/automation/autonomousRunner.d.ts +5 -5
- package/dist/automation/runnerTypes.d.ts +1 -1
- package/dist/automation/runnerTypes.d.ts.map +1 -1
- package/dist/cli/authHandler.d.ts +16 -0
- package/dist/cli/authHandler.d.ts.map +1 -0
- package/dist/cli/authHandler.js +93 -0
- package/dist/cli/authHandler.js.map +1 -0
- package/dist/cli/checkHandler.d.ts +25 -0
- package/dist/cli/checkHandler.d.ts.map +1 -0
- package/dist/cli/checkHandler.js +465 -0
- package/dist/cli/checkHandler.js.map +1 -0
- package/dist/cli.js +69 -0
- package/dist/cli.js.map +1 -1
- package/dist/core/config.d.ts +17 -4
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +21 -8
- package/dist/core/config.js.map +1 -1
- package/dist/core/service.d.ts.map +1 -1
- package/dist/core/service.js +18 -8
- package/dist/core/service.js.map +1 -1
- package/dist/core/types.d.ts +4 -2
- package/dist/core/types.d.ts.map +1 -1
- package/dist/issues/graphql/resolvers.d.ts +252 -0
- package/dist/issues/graphql/resolvers.d.ts.map +1 -0
- package/dist/issues/graphql/resolvers.js +88 -0
- package/dist/issues/graphql/resolvers.js.map +1 -0
- package/dist/issues/graphql/server.d.ts +13 -0
- package/dist/issues/graphql/server.d.ts.map +1 -0
- package/dist/issues/graphql/server.js +56 -0
- package/dist/issues/graphql/server.js.map +1 -0
- package/dist/issues/graphql/typeDefs.d.ts +2 -0
- package/dist/issues/graphql/typeDefs.d.ts.map +1 -0
- package/dist/issues/graphql/typeDefs.js +251 -0
- package/dist/issues/graphql/typeDefs.js.map +1 -0
- package/dist/issues/index.d.ts +8 -0
- package/dist/issues/index.d.ts.map +1 -0
- package/dist/issues/index.js +11 -0
- package/dist/issues/index.js.map +1 -0
- package/dist/issues/issueBoardHtml.d.ts +2 -0
- package/dist/issues/issueBoardHtml.d.ts.map +1 -0
- package/dist/issues/issueBoardHtml.js +677 -0
- package/dist/issues/issueBoardHtml.js.map +1 -0
- package/dist/issues/linearBridge.d.ts +27 -0
- package/dist/issues/linearBridge.d.ts.map +1 -0
- package/dist/issues/linearBridge.js +211 -0
- package/dist/issues/linearBridge.js.map +1 -0
- package/dist/issues/memoryBridge.d.ts +35 -0
- package/dist/issues/memoryBridge.d.ts.map +1 -0
- package/dist/issues/memoryBridge.js +184 -0
- package/dist/issues/memoryBridge.js.map +1 -0
- package/dist/issues/schema.d.ts +162 -0
- package/dist/issues/schema.d.ts.map +1 -0
- package/dist/issues/schema.js +121 -0
- package/dist/issues/schema.js.map +1 -0
- package/dist/issues/sqliteStore.d.ts +90 -0
- package/dist/issues/sqliteStore.d.ts.map +1 -0
- package/dist/issues/sqliteStore.js +488 -0
- package/dist/issues/sqliteStore.js.map +1 -0
- package/dist/knowledge/index.d.ts.map +1 -1
- package/dist/knowledge/index.js +9 -3
- package/dist/knowledge/index.js.map +1 -1
- package/dist/linear/linear.d.ts +4 -0
- package/dist/linear/linear.d.ts.map +1 -1
- package/dist/linear/linear.js +27 -0
- package/dist/linear/linear.js.map +1 -1
- package/dist/registry/bsDetector.d.ts +24 -0
- package/dist/registry/bsDetector.d.ts.map +1 -0
- package/dist/registry/bsDetector.js +276 -0
- package/dist/registry/bsDetector.js.map +1 -0
- package/dist/registry/entityScanner.d.ts +36 -0
- package/dist/registry/entityScanner.d.ts.map +1 -0
- package/dist/registry/entityScanner.js +693 -0
- package/dist/registry/entityScanner.js.map +1 -0
- package/dist/registry/graphql/resolvers.d.ts +778 -0
- package/dist/registry/graphql/resolvers.d.ts.map +1 -0
- package/dist/registry/graphql/resolvers.js +127 -0
- package/dist/registry/graphql/resolvers.js.map +1 -0
- package/dist/registry/graphql/typeDefs.d.ts +2 -0
- package/dist/registry/graphql/typeDefs.d.ts.map +1 -0
- package/dist/registry/graphql/typeDefs.js +276 -0
- package/dist/registry/graphql/typeDefs.js.map +1 -0
- package/dist/registry/index.d.ts +12 -0
- package/dist/registry/index.d.ts.map +1 -0
- package/dist/registry/index.js +18 -0
- package/dist/registry/index.js.map +1 -0
- package/dist/registry/issueBridge.d.ts +8 -0
- package/dist/registry/issueBridge.d.ts.map +1 -0
- package/dist/registry/issueBridge.js +30 -0
- package/dist/registry/issueBridge.js.map +1 -0
- package/dist/registry/memoryBridge.d.ts +13 -0
- package/dist/registry/memoryBridge.d.ts.map +1 -0
- package/dist/registry/memoryBridge.js +60 -0
- package/dist/registry/memoryBridge.js.map +1 -0
- package/dist/registry/schema.d.ts +307 -0
- package/dist/registry/schema.d.ts.map +1 -0
- package/dist/registry/schema.js +139 -0
- package/dist/registry/schema.js.map +1 -0
- package/dist/registry/sqliteStore.d.ts +101 -0
- package/dist/registry/sqliteStore.d.ts.map +1 -0
- package/dist/registry/sqliteStore.js +688 -0
- package/dist/registry/sqliteStore.js.map +1 -0
- package/dist/support/chatBackend.d.ts.map +1 -1
- package/dist/support/chatBackend.js +35 -4
- package/dist/support/chatBackend.js.map +1 -1
- package/dist/support/chatTui.d.ts.map +1 -1
- package/dist/support/chatTui.js +109 -3
- package/dist/support/chatTui.js.map +1 -1
- package/dist/support/dashboardHtml.d.ts +1 -1
- package/dist/support/dashboardHtml.d.ts.map +1 -1
- package/dist/support/dashboardHtml.js +1 -0
- package/dist/support/dashboardHtml.js.map +1 -1
- package/dist/support/web.d.ts.map +1 -1
- package/dist/support/web.js +16 -3
- package/dist/support/web.js.map +1 -1
- package/package.json +9 -3
- package/templates/TOOLS.md +2 -2
package/README.md
CHANGED
|
@@ -1,134 +1,100 @@
|
|
|
1
1
|
# OpenSwarm
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@intrect/openswarm)
|
|
4
|
+
[](https://www.npmjs.com/package/@intrect/openswarm)
|
|
5
|
+
[](LICENSE)
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
> Autonomous AI agent orchestrator — Claude, GPT, Codex, and local models (Ollama/LMStudio/llama.cpp)
|
|
6
8
|
|
|
7
|
-
|
|
9
|
+
OpenSwarm orchestrates multiple AI agents as autonomous code workers. It picks up Linear issues, runs Worker/Reviewer pair pipelines, reports to Discord, and retains long-term memory via LanceDB. Supports Claude Code, OpenAI GPT, Codex, and **local open-source models** via Ollama, LMStudio, or llama.cpp.
|
|
8
10
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-

|
|
12
|
-
|
|
13
|
-
Real-time supervisor dashboard with repository status, pipeline events, live logs, PR processor, and agent chat.
|
|
14
|
-
|
|
15
|
-
### CLI Chat Interface
|
|
16
|
-
|
|
17
|
-
**Rich TUI Mode** (Claude Code inspired):
|
|
11
|
+
## Quick Start
|
|
18
12
|
|
|
19
13
|
```bash
|
|
20
|
-
|
|
14
|
+
npm install -g @intrect/openswarm
|
|
15
|
+
openswarm
|
|
21
16
|
```
|
|
22
17
|
|
|
23
|
-
|
|
18
|
+
That's it. `openswarm` with no arguments launches the TUI chat interface immediately.
|
|
24
19
|
|
|
25
|
-
|
|
26
|
-
- 5 interactive tabs: Chat, Projects, Tasks, Stuck, Logs
|
|
27
|
-
- Real-time streaming responses with themed loading messages
|
|
28
|
-
- Keyboard shortcuts: Tab (switch tabs), Enter (send), Shift+Enter (newline), Esc (exit input)
|
|
29
|
-
- Session management: auto-save, resume, model switching
|
|
30
|
-
- Status bar: model, message count, cumulative cost
|
|
20
|
+

|
|
31
21
|
|
|
32
|
-
|
|
22
|
+
### TUI keyboard shortcuts
|
|
33
23
|
|
|
34
|
-
|
|
35
|
-
|
|
24
|
+
| Key | Action |
|
|
25
|
+
|-----|--------|
|
|
26
|
+
| `Tab` | Switch tabs (Chat / Projects / Tasks / Stuck / Logs) |
|
|
27
|
+
| `Enter` | Send message |
|
|
28
|
+
| `Shift+Enter` | Newline |
|
|
29
|
+
| `i` | Focus input |
|
|
30
|
+
| `Esc` | Exit input focus |
|
|
31
|
+
| `Ctrl+C` | Quit |
|
|
36
32
|
|
|
37
|
-
|
|
38
|
-
║ Swarm Chat sonnet-4-5 ║
|
|
39
|
-
╚════════════════════════════════════╝
|
|
40
|
-
demo | /help | Ctrl+D exit
|
|
33
|
+
Status bar shows: provider · model · message count · cumulative cost
|
|
41
34
|
|
|
42
|
-
|
|
43
|
-
assistant OpenSwarm has 9 main architectural layers... ($0.0023)
|
|
35
|
+
---
|
|
44
36
|
|
|
45
|
-
|
|
46
|
-
Model: claude-haiku-4-5-20251001
|
|
37
|
+
## CLI Commands
|
|
47
38
|
|
|
48
|
-
|
|
49
|
-
|
|
39
|
+
```bash
|
|
40
|
+
openswarm # TUI chat (default)
|
|
41
|
+
openswarm chat [session] # Simple readline chat
|
|
42
|
+
openswarm start # Start full daemon (requires config.yaml)
|
|
43
|
+
openswarm run "Fix the bug" -p ~/my-project # Run a single task
|
|
44
|
+
openswarm exec "Run tests" --local --pipeline # Execute via daemon
|
|
45
|
+
openswarm init # Generate config.yaml scaffold
|
|
46
|
+
openswarm validate # Validate config.yaml
|
|
47
|
+
|
|
48
|
+
# Code Registry & BS Detector
|
|
49
|
+
openswarm check --scan # Scan repo → register all entities
|
|
50
|
+
openswarm check src/foo.ts # File brief (entities, tests, risk)
|
|
51
|
+
openswarm check --bs # BS pattern scan (bad code smells)
|
|
52
|
+
openswarm check --stats # Registry statistics
|
|
53
|
+
openswarm check --high-risk # High-risk entities
|
|
54
|
+
openswarm check --search "name" # Full-text search
|
|
55
|
+
openswarm annotate "funcName" --deprecate "reason"
|
|
56
|
+
openswarm annotate "funcName" --tag "needs-refactor"
|
|
57
|
+
openswarm annotate "funcName" --warn "error/security: SQL injection"
|
|
50
58
|
```
|
|
51
59
|
|
|
52
|
-
|
|
60
|
+
### `openswarm exec` options
|
|
53
61
|
|
|
54
|
-
|
|
62
|
+
| Option | Description |
|
|
63
|
+
|--------|-------------|
|
|
64
|
+
| `--path <path>` | Project path (default: cwd) |
|
|
65
|
+
| `--timeout <seconds>` | Timeout in seconds (default: 600) |
|
|
66
|
+
| `--local` | Execute locally without daemon |
|
|
67
|
+
| `--pipeline` | Full pipeline: worker + reviewer + tester + documenter |
|
|
68
|
+
| `--worker-only` | Worker only, no review |
|
|
69
|
+
| `-m, --model <model>` | Model override for worker |
|
|
55
70
|
|
|
56
|
-
|
|
57
|
-
┌──────────────────────────┐
|
|
58
|
-
│ Linear API │
|
|
59
|
-
│ (issues, state, memory) │
|
|
60
|
-
└─────────────┬────────────┘
|
|
61
|
-
│
|
|
62
|
-
┌─────────────────────┼─────────────────────┐
|
|
63
|
-
│ │ │
|
|
64
|
-
v v v
|
|
65
|
-
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
|
|
66
|
-
│ AutonomousRunner │ │ DecisionEngine │ │ TaskScheduler │
|
|
67
|
-
│ (heartbeat loop) │─>│ (scope guard) │─>│ (queue + slots) │
|
|
68
|
-
└────────┬─────────┘ └──────────────────┘ └────────┬─────────┘
|
|
69
|
-
│ │
|
|
70
|
-
v v
|
|
71
|
-
┌──────────────────────────────────────────────────────────────┐
|
|
72
|
-
│ PairPipeline │
|
|
73
|
-
│ ┌────────┐ ┌──────────┐ ┌────────┐ ┌─────────────┐ │
|
|
74
|
-
│ │ Worker │──>│ Reviewer │──>│ Tester │──>│ Documenter │ │
|
|
75
|
-
│ │(Adapter│<──│(Adapter) │ │(Adapter│ │ (Adapter) │ │
|
|
76
|
-
│ └───┬────┘ └──────────┘ └────────┘ └─────────────┘ │
|
|
77
|
-
│ │ ↕ StuckDetector │
|
|
78
|
-
│ ┌───┴────────────────────────────────────────────────────┐ │
|
|
79
|
-
│ │ CLI Adapters: Claude (`claude -p`) | Codex (`codex`) │ │
|
|
80
|
-
│ └────────────────────────────────────────────────────────┘ │
|
|
81
|
-
└──────────────────────────────────────────────────────────────┘
|
|
82
|
-
│ │ │
|
|
83
|
-
v v v
|
|
84
|
-
┌──────────────┐ ┌──────────────────┐ ┌──────────────────┐
|
|
85
|
-
│ Discord Bot │ │ Memory (LanceDB │ │ Knowledge Graph │
|
|
86
|
-
│ (commands) │ │ + Xenova E5) │ │ (code analysis) │
|
|
87
|
-
└──────────────┘ └──────────────────┘ └──────────────────┘
|
|
88
|
-
```
|
|
71
|
+
Exit codes: `0` success · `1` failure · `2` timeout
|
|
89
72
|
|
|
90
|
-
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Full Daemon Setup
|
|
91
76
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
- **Decision Engine** - Scope validation, rate limiting, priority-based task selection, and workflow mapping
|
|
96
|
-
- **Cognitive Memory** - LanceDB vector store with Xenova/multilingual-e5-base embeddings for long-term recall across sessions
|
|
97
|
-
- **Knowledge Graph** - Static code analysis, dependency mapping, impact analysis, and file-level conflict detection across concurrent tasks
|
|
98
|
-
- **Discord Control** - Full command interface for monitoring, task dispatch, scheduling, provider switching, and pair session management
|
|
99
|
-
- **Rich TUI Chat** - Claude Code inspired terminal interface with tabs, streaming responses, and geek-themed loading messages
|
|
100
|
-
- **Dynamic Scheduling** - Cron-based job scheduler with Discord management commands
|
|
101
|
-
- **PR Auto-Improvement** - Monitors open PRs, auto-fixes CI failures, auto-resolves merge conflicts, and retries until all checks pass
|
|
102
|
-
- **Long-Running Monitors** - Track external processes (training jobs, batch tasks) and report completion
|
|
103
|
-
- **Web Dashboard** - Real-time pipeline stages, cost tracking, worktree status, and live logs on port 3847
|
|
104
|
-
- **Pace Control** - 5-hour rolling window task caps, per-project limits, turbo mode, exponential backoff on failures
|
|
105
|
-
- **i18n** - English and Korean locale support
|
|
106
|
-
|
|
107
|
-
## Prerequisites
|
|
77
|
+
For autonomous operation (Linear issue processing, Discord control, PR auto-improvement), you need a full config:
|
|
78
|
+
|
|
79
|
+
### Prerequisites
|
|
108
80
|
|
|
109
81
|
- **Node.js** >= 22
|
|
110
|
-
- **CLI
|
|
111
|
-
|
|
112
|
-
- **OpenAI Codex CLI** installed (`codex exec`) — alternative provider
|
|
82
|
+
- **Claude Code CLI** authenticated (`claude -p`) — default provider
|
|
83
|
+
- **OpenAI Codex CLI** (`codex exec`) — optional alternative provider
|
|
113
84
|
- **Discord Bot** token with message content intent
|
|
114
85
|
- **Linear** API key and team ID
|
|
115
86
|
- **GitHub CLI** (`gh`) for CI monitoring (optional)
|
|
116
87
|
|
|
117
|
-
|
|
88
|
+
### Configuration
|
|
118
89
|
|
|
119
90
|
```bash
|
|
120
91
|
git clone https://github.com/unohee/OpenSwarm.git
|
|
121
92
|
cd OpenSwarm
|
|
122
93
|
npm install
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
## Configuration
|
|
126
|
-
|
|
127
|
-
```bash
|
|
128
94
|
cp config.example.yaml config.yaml
|
|
129
95
|
```
|
|
130
96
|
|
|
131
|
-
Create a `.env` file
|
|
97
|
+
Create a `.env` file:
|
|
132
98
|
|
|
133
99
|
```bash
|
|
134
100
|
DISCORD_TOKEN=your-discord-bot-token
|
|
@@ -137,9 +103,9 @@ LINEAR_API_KEY=your-linear-api-key
|
|
|
137
103
|
LINEAR_TEAM_ID=your-linear-team-id
|
|
138
104
|
```
|
|
139
105
|
|
|
140
|
-
`config.yaml` supports
|
|
106
|
+
`config.yaml` supports `${VAR}` / `${VAR:-default}` substitution and is validated with Zod schemas.
|
|
141
107
|
|
|
142
|
-
### Key
|
|
108
|
+
### Key configuration sections
|
|
143
109
|
|
|
144
110
|
| Section | Description |
|
|
145
111
|
|---------|-------------|
|
|
@@ -152,20 +118,22 @@ LINEAR_TEAM_ID=your-linear-team-id
|
|
|
152
118
|
|
|
153
119
|
### CLI Adapter (Provider)
|
|
154
120
|
|
|
155
|
-
OpenSwarm supports multiple CLI backends. Set the default in `config.yaml`:
|
|
156
|
-
|
|
157
121
|
```yaml
|
|
158
|
-
adapter: claude # "claude"
|
|
122
|
+
adapter: claude # "claude" | "codex" | "gpt" | "local"
|
|
159
123
|
```
|
|
160
124
|
|
|
161
125
|
Switch at runtime via Discord: `!provider codex` / `!provider claude`
|
|
162
126
|
|
|
163
|
-
| Adapter |
|
|
164
|
-
|
|
165
|
-
| `claude` |
|
|
166
|
-
| `codex` |
|
|
127
|
+
| Adapter | Backend | Models | Auth |
|
|
128
|
+
|---------|---------|--------|------|
|
|
129
|
+
| `claude` | Claude Code CLI | sonnet-4, haiku-4.5, opus-4 | CLI auth |
|
|
130
|
+
| `codex` | OpenAI Codex CLI | o3, o4-mini | CLI auth |
|
|
131
|
+
| `gpt` | OpenAI API | gpt-4o, o3, gpt-4.1 | OAuth PKCE |
|
|
132
|
+
| `local` | Ollama / LMStudio / llama.cpp | gemma4, llama3, mistral, qwen, etc. | None |
|
|
133
|
+
|
|
134
|
+
Local models are auto-detected on standard ports (Ollama `:11434`, LMStudio `:1234`, llama.cpp `:8080`).
|
|
167
135
|
|
|
168
|
-
Per-role adapter overrides
|
|
136
|
+
Per-role adapter overrides:
|
|
169
137
|
|
|
170
138
|
```yaml
|
|
171
139
|
autonomous:
|
|
@@ -180,8 +148,6 @@ autonomous:
|
|
|
180
148
|
|
|
181
149
|
### Agent Roles
|
|
182
150
|
|
|
183
|
-
Each pipeline stage can be configured independently:
|
|
184
|
-
|
|
185
151
|
```yaml
|
|
186
152
|
autonomous:
|
|
187
153
|
defaultRoles:
|
|
@@ -201,158 +167,120 @@ autonomous:
|
|
|
201
167
|
enabled: false
|
|
202
168
|
```
|
|
203
169
|
|
|
204
|
-
|
|
170
|
+
### Running the daemon
|
|
205
171
|
|
|
206
|
-
|
|
172
|
+
#### macOS launchd service (recommended)
|
|
207
173
|
|
|
208
174
|
```bash
|
|
209
|
-
#
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
# Execute via daemon (auto-starts service if needed)
|
|
219
|
-
openswarm exec "Run tests and fix failures" --worker-only
|
|
220
|
-
openswarm exec "Review all pending PRs" --timeout 300
|
|
221
|
-
openswarm exec "Fix CI" --local --pipeline
|
|
222
|
-
|
|
223
|
-
# Initialize configuration
|
|
224
|
-
openswarm init
|
|
225
|
-
|
|
226
|
-
# Validate configuration
|
|
227
|
-
openswarm validate
|
|
228
|
-
|
|
229
|
-
# Start the full daemon
|
|
230
|
-
openswarm start
|
|
175
|
+
npm run service:install # Build and install as system service
|
|
176
|
+
npm run service:start # Start
|
|
177
|
+
npm run service:stop # Stop
|
|
178
|
+
npm run service:restart # Restart
|
|
179
|
+
npm run service:status # Status and recent logs
|
|
180
|
+
npm run service:logs # stdout (follow mode)
|
|
181
|
+
npm run service:errors # stderr (follow mode)
|
|
182
|
+
npm run service:uninstall # Uninstall
|
|
231
183
|
```
|
|
232
184
|
|
|
233
|
-
####
|
|
185
|
+
#### Manual
|
|
234
186
|
|
|
235
|
-
| Option | Description |
|
|
236
|
-
|--------|-------------|
|
|
237
|
-
| `--path <path>` | Project path (default: cwd) |
|
|
238
|
-
| `--timeout <seconds>` | Timeout in seconds (default: 600) |
|
|
239
|
-
| `--no-auto-start` | Do not auto-start the service |
|
|
240
|
-
| `--local` | Execute locally without daemon |
|
|
241
|
-
| `--pipeline` | Full pipeline: worker + reviewer + tester + documenter |
|
|
242
|
-
| `--worker-only` | Worker only, no review |
|
|
243
|
-
| `-m, --model <model>` | Model override for worker |
|
|
244
|
-
|
|
245
|
-
Exit codes: `0` (success), `1` (failure), `2` (timeout).
|
|
246
|
-
|
|
247
|
-
### Running the Service
|
|
248
|
-
|
|
249
|
-
#### macOS launchd Service (Recommended)
|
|
250
|
-
|
|
251
|
-
**Installation:**
|
|
252
187
|
```bash
|
|
253
|
-
|
|
254
|
-
npm run
|
|
188
|
+
npm run build && npm start # Production
|
|
189
|
+
npm run dev # Development (tsx watch)
|
|
190
|
+
docker compose up -d # Docker
|
|
255
191
|
```
|
|
256
192
|
|
|
257
|
-
|
|
258
|
-
```bash
|
|
259
|
-
npm run service:start # Start service
|
|
260
|
-
npm run service:stop # Stop service
|
|
261
|
-
npm run service:restart # Restart service
|
|
262
|
-
npm run service:status # View status and recent logs
|
|
263
|
-
npm run service:logs # View stdout logs (follow mode)
|
|
264
|
-
npm run service:errors # View stderr logs (follow mode)
|
|
265
|
-
npm run service:uninstall # Uninstall service
|
|
266
|
-
```
|
|
193
|
+
---
|
|
267
194
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
195
|
+
## Architecture
|
|
196
|
+
|
|
197
|
+
```
|
|
198
|
+
┌──────────────────────────┐
|
|
199
|
+
│ Linear API │
|
|
200
|
+
│ (issues, state, memory) │
|
|
201
|
+
└─────────────┬────────────┘
|
|
202
|
+
│
|
|
203
|
+
┌─────────────────────┼─────────────────────┐
|
|
204
|
+
│ │ │
|
|
205
|
+
v v v
|
|
206
|
+
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
|
|
207
|
+
│ AutonomousRunner │ │ DecisionEngine │ │ TaskScheduler │
|
|
208
|
+
│ (heartbeat loop) │─>│ (scope guard) │─>│ (queue + slots) │
|
|
209
|
+
└────────┬─────────┘ └──────────────────┘ └────────┬─────────┘
|
|
210
|
+
│ │
|
|
211
|
+
v v
|
|
212
|
+
┌──────────────────────────────────────────────────────────────┐
|
|
213
|
+
│ PairPipeline │
|
|
214
|
+
│ ┌────────┐ ┌──────────┐ ┌────────┐ ┌─────────────┐ │
|
|
215
|
+
│ │ Worker │──>│ Reviewer │──>│ Tester │──>│ Documenter │ │
|
|
216
|
+
│ │(Adapter│<──│(Adapter) │ │(Adapter│ │ (Adapter) │ │
|
|
217
|
+
│ └───┬────┘ └──────────┘ └────────┘ └─────────────┘ │
|
|
218
|
+
│ │ ↕ StuckDetector │
|
|
219
|
+
│ ┌───┴────────────────────────────────────────────────────┐ │
|
|
220
|
+
│ │ Adapters: Claude | Codex | GPT | Local (Ollama/LMS) │ │
|
|
221
|
+
│ └────────────────────────────────────────────────────────┘ │
|
|
222
|
+
└──────────────────────────────────────────────────────────────┘
|
|
223
|
+
│ │ │
|
|
224
|
+
v v v
|
|
225
|
+
┌──────────────┐ ┌──────────────────┐ ┌──────────────────┐
|
|
226
|
+
│ Discord Bot │ │ Memory (LanceDB │ │ Knowledge Graph │
|
|
227
|
+
│ (commands) │ │ + Xenova E5) │ │ (code analysis) │
|
|
228
|
+
└──────────────┘ └──────────────────┘ └────────┬─────────┘
|
|
229
|
+
│
|
|
230
|
+
┌────────┴─────────┐
|
|
231
|
+
│ Code Registry │
|
|
232
|
+
│ (SQLite + FTS5) │
|
|
233
|
+
│ + BS Detector │
|
|
234
|
+
└──────────────────┘
|
|
272
235
|
```
|
|
273
236
|
|
|
274
|
-
|
|
275
|
-
- Auto-start on system boot
|
|
276
|
-
- Auto-restart on crash
|
|
277
|
-
- Log to `~/.openswarm/logs/`
|
|
278
|
-
- Run with your user permissions (access to Claude CLI, gh, local files)
|
|
279
|
-
- (Optional) Open web dashboard at http://localhost:3847 on boot
|
|
237
|
+
## Features
|
|
280
238
|
|
|
281
|
-
|
|
239
|
+
- **Multi-Provider Adapters** — Pluggable adapter system: **Claude Code**, **OpenAI GPT/Codex**, and **local models** (Ollama, LMStudio, llama.cpp) with runtime provider switching
|
|
240
|
+
- **Code Registry** — SQLite-backed entity registry tracking every function/class/type across 8 languages, with complexity scoring, test mapping, and risk assessment
|
|
241
|
+
- **BS Detector** — Built-in static analysis engine that detects bad code patterns (empty catch, hardcoded secrets, `as any`, etc.) with pipeline guard integration
|
|
242
|
+
- **Autonomous Pipeline** — Cron-driven heartbeat fetches Linear issues, runs Worker/Reviewer pair loops, and updates issue state automatically
|
|
243
|
+
- **Worker/Reviewer Pairs** — Multi-iteration code generation with automated review, testing, and documentation stages
|
|
244
|
+
- **Decision Engine** — Scope validation, rate limiting, priority-based task selection, and workflow mapping
|
|
245
|
+
- **Cognitive Memory** — LanceDB vector store with Xenova/multilingual-e5-base embeddings for long-term recall across sessions
|
|
246
|
+
- **Knowledge Graph** — Static code analysis, dependency mapping, impact analysis, and file-level conflict detection across concurrent tasks
|
|
247
|
+
- **Discord Control** — Full command interface for monitoring, task dispatch, scheduling, provider switching, and pair session management
|
|
248
|
+
- **Rich TUI Chat** — Claude Code inspired terminal interface with tabs, streaming responses, and geek-themed loading messages
|
|
249
|
+
- **Dynamic Scheduling** — Cron-based job scheduler with Discord management commands
|
|
250
|
+
- **PR Auto-Improvement** — Monitors open PRs, auto-fixes CI failures, auto-resolves merge conflicts, and retries until all checks pass
|
|
251
|
+
- **Long-Running Monitors** — Track external processes (training jobs, batch tasks) and report completion
|
|
252
|
+
- **Web Dashboard** — Real-time pipeline stages, cost tracking, worktree status, and live logs on port 3847
|
|
253
|
+
- **Pace Control** — 5-hour rolling window task caps, per-project limits, turbo mode, exponential backoff on failures
|
|
254
|
+
- **i18n** — English and Korean locale support
|
|
282
255
|
|
|
283
|
-
|
|
284
|
-
# Development
|
|
285
|
-
npm run dev
|
|
256
|
+
---
|
|
286
257
|
|
|
287
|
-
|
|
288
|
-
npm run build
|
|
289
|
-
npm start
|
|
258
|
+
## How It Works
|
|
290
259
|
|
|
291
|
-
# Background (legacy)
|
|
292
|
-
nohup npm start > openswarm.log 2>&1 &
|
|
293
260
|
```
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
261
|
+
Linear (Todo/In Progress)
|
|
262
|
+
→ Fetch assigned issues
|
|
263
|
+
→ DecisionEngine filters & prioritizes
|
|
264
|
+
→ Resolve project path via projectMapper
|
|
265
|
+
→ PairPipeline.run()
|
|
266
|
+
→ Worker generates code (Claude CLI)
|
|
267
|
+
→ Reviewer evaluates (APPROVE/REVISE/REJECT)
|
|
268
|
+
→ Loop up to N iterations
|
|
269
|
+
→ Optional: Tester → Documenter stages
|
|
270
|
+
→ Update Linear issue state (Done/Blocked)
|
|
271
|
+
→ Report to Discord
|
|
272
|
+
→ Save to cognitive memory
|
|
299
273
|
```
|
|
300
274
|
|
|
301
|
-
###
|
|
275
|
+
### Memory System
|
|
302
276
|
|
|
303
|
-
|
|
277
|
+
Hybrid retrieval: `0.55 × similarity + 0.20 × importance + 0.15 × recency + 0.10 × frequency`
|
|
304
278
|
|
|
305
|
-
|
|
306
|
-
openswarm() {
|
|
307
|
-
case "$1" in
|
|
308
|
-
start)
|
|
309
|
-
cd /path/to/OpenSwarm && nohup npm start > ~/.openswarm/log 2>&1 &
|
|
310
|
-
echo "✅ Started (PID: $!)"
|
|
311
|
-
;;
|
|
312
|
-
stop)
|
|
313
|
-
pkill -f "openswarm" && echo "✅ Stopped"
|
|
314
|
-
;;
|
|
315
|
-
status)
|
|
316
|
-
pgrep -f "openswarm" && echo "✅ Running" || echo "❌ Stopped"
|
|
317
|
-
;;
|
|
318
|
-
chat)
|
|
319
|
-
cd /path/to/OpenSwarm && node --import=tsx src/cli.ts chat "${@:2}"
|
|
320
|
-
;;
|
|
321
|
-
esac
|
|
322
|
-
}
|
|
323
|
-
```
|
|
279
|
+
Memory types: `belief` · `strategy` · `user_model` · `system_pattern` · `constraint`
|
|
324
280
|
|
|
325
|
-
|
|
281
|
+
Background: decay, consolidation, contradiction detection, distillation.
|
|
326
282
|
|
|
327
|
-
|
|
328
|
-
src/
|
|
329
|
-
├── index.ts # Entry point
|
|
330
|
-
├── cli.ts # CLI entry point (run, exec, chat, init, validate, start)
|
|
331
|
-
├── cli/ # CLI subcommand handlers
|
|
332
|
-
│ └── promptHandler.ts # `exec` command: daemon submit, auto-start, polling
|
|
333
|
-
├── core/ # Config, service lifecycle, types, event hub
|
|
334
|
-
├── adapters/ # CLI provider adapters (claude, codex), process registry
|
|
335
|
-
├── agents/ # Worker, reviewer, tester, documenter, auditor
|
|
336
|
-
│ ├── pairPipeline.ts # Worker → Reviewer → Tester → Documenter pipeline
|
|
337
|
-
│ ├── agentBus.ts # Inter-agent message bus
|
|
338
|
-
│ └── cliStreamParser.ts # Claude CLI output parser
|
|
339
|
-
├── orchestration/ # Decision engine, task parser, scheduler, workflow
|
|
340
|
-
├── automation/ # Autonomous runner, cron scheduler, PR processor
|
|
341
|
-
│ ├── autonomousRunner.ts # Cron-driven heartbeat and task dispatch
|
|
342
|
-
│ ├── prProcessor.ts # PR auto-improvement (CI fixes, conflict resolution)
|
|
343
|
-
│ ├── conflictResolver.ts # AI-powered merge conflict resolution
|
|
344
|
-
│ ├── prOwnership.ts # Bot PR tracking for conflict resolution
|
|
345
|
-
│ ├── longRunningMonitor.ts# External process monitoring
|
|
346
|
-
│ └── runnerState.ts # Persistent pipeline state
|
|
347
|
-
├── memory/ # LanceDB + Xenova embeddings cognitive memory
|
|
348
|
-
├── knowledge/ # Code knowledge graph (scanner, analyzer, graph)
|
|
349
|
-
├── discord/ # Bot core, command handlers, pair session UI
|
|
350
|
-
├── linear/ # Linear SDK wrapper, project updater
|
|
351
|
-
├── github/ # GitHub CLI wrapper for CI monitoring
|
|
352
|
-
├── support/ # Web dashboard, planner, rollback, git tools
|
|
353
|
-
├── locale/ # i18n (en/ko) with prompt templates
|
|
354
|
-
└── __tests__/ # Vitest test suite
|
|
355
|
-
```
|
|
283
|
+
---
|
|
356
284
|
|
|
357
285
|
## Discord Commands
|
|
358
286
|
|
|
@@ -416,95 +344,50 @@ src/
|
|
|
416
344
|
| `!memory search "<query>"` | Search cognitive memory |
|
|
417
345
|
| `!help` | Full command reference |
|
|
418
346
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
### Issue Processing Flow
|
|
422
|
-
|
|
423
|
-
```
|
|
424
|
-
Linear (Todo/In Progress)
|
|
425
|
-
→ Fetch assigned issues
|
|
426
|
-
→ DecisionEngine filters & prioritizes
|
|
427
|
-
→ Resolve project path via projectMapper
|
|
428
|
-
→ PairPipeline.run()
|
|
429
|
-
→ Worker generates code (Claude CLI)
|
|
430
|
-
→ Reviewer evaluates (APPROVE/REVISE/REJECT)
|
|
431
|
-
→ Loop up to N iterations
|
|
432
|
-
→ Optional: Tester → Documenter stages
|
|
433
|
-
→ Update Linear issue state (Done/Blocked)
|
|
434
|
-
→ Report to Discord
|
|
435
|
-
→ Save to cognitive memory
|
|
436
|
-
```
|
|
437
|
-
|
|
438
|
-
### Memory System
|
|
439
|
-
|
|
440
|
-
Hybrid retrieval scoring: `0.55 * similarity + 0.20 * importance + 0.15 * recency + 0.10 * frequency`
|
|
441
|
-
|
|
442
|
-
Memory types: `belief`, `strategy`, `user_model`, `system_pattern`, `constraint`
|
|
443
|
-
|
|
444
|
-
Background cognition: decay, consolidation, contradiction detection, and distillation (noise filtering).
|
|
445
|
-
|
|
446
|
-
## `@intrect/claude-driver` (npm 패키지)
|
|
447
|
-
|
|
448
|
-
OpenSwarm의 Claude CLI 스폰 + 스트리밍 파싱 로직을 독립 패키지로 추출했습니다. 다른 프로젝트에서 OpenSwarm 없이 바로 사용할 수 있습니다.
|
|
449
|
-
|
|
450
|
-
```bash
|
|
451
|
-
npm install @intrect/claude-driver
|
|
452
|
-
```
|
|
453
|
-
|
|
454
|
-
### 기본 사용법
|
|
455
|
-
|
|
456
|
-
```ts
|
|
457
|
-
import { run } from '@intrect/claude-driver';
|
|
458
|
-
|
|
459
|
-
const result = await run({
|
|
460
|
-
prompt: 'Fix the bug in src/app.ts',
|
|
461
|
-
cwd: '/my/project',
|
|
462
|
-
});
|
|
463
|
-
console.log(result.response); // 어시스턴트 응답
|
|
464
|
-
console.log(result.cost); // "$0.03 | 1.2k in / 800 out | 12.3s"
|
|
465
|
-
```
|
|
466
|
-
|
|
467
|
-
### 어댑터 직접 사용 (mid-level)
|
|
347
|
+
---
|
|
468
348
|
|
|
469
|
-
|
|
470
|
-
import { spawnCli, ClaudeCliAdapter } from '@intrect/claude-driver';
|
|
349
|
+
## Project Structure
|
|
471
350
|
|
|
472
|
-
const adapter = new ClaudeCliAdapter();
|
|
473
|
-
const raw = await spawnCli(adapter, {
|
|
474
|
-
prompt: 'Explain this code',
|
|
475
|
-
cwd: '/my/project',
|
|
476
|
-
onLog: (line) => process.stdout.write(line),
|
|
477
|
-
});
|
|
478
351
|
```
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
352
|
+
src/
|
|
353
|
+
├── index.ts # Entry point
|
|
354
|
+
├── cli.ts # CLI entry point (run, exec, chat, init, validate, start)
|
|
355
|
+
├── cli/ # CLI subcommand handlers
|
|
356
|
+
│ └── promptHandler.ts # exec command: daemon submit, auto-start, polling
|
|
357
|
+
├── core/ # Config, service lifecycle, types, event hub
|
|
358
|
+
├── adapters/ # CLI provider adapters (claude, codex, gpt, local), process registry
|
|
359
|
+
├── agents/ # Worker, reviewer, tester, documenter, auditor
|
|
360
|
+
│ ├── pairPipeline.ts # Worker → Reviewer → Tester → Documenter pipeline
|
|
361
|
+
│ ├── agentBus.ts # Inter-agent message bus
|
|
362
|
+
│ └── cliStreamParser.ts # Claude CLI output parser
|
|
363
|
+
├── orchestration/ # Decision engine, task parser, scheduler, workflow
|
|
364
|
+
├── automation/ # Autonomous runner, cron scheduler, PR processor
|
|
365
|
+
├── memory/ # LanceDB + Xenova embeddings cognitive memory
|
|
366
|
+
├── knowledge/ # Code knowledge graph (scanner, analyzer, graph)
|
|
367
|
+
├── registry/ # Code entity registry, BS detector, entity scanner
|
|
368
|
+
├── issues/ # Local issue tracker (SQLite + GraphQL + Kanban UI)
|
|
369
|
+
├── discord/ # Bot core, command handlers, pair session UI
|
|
370
|
+
├── linear/ # Linear SDK wrapper, project updater
|
|
371
|
+
├── github/ # GitHub CLI wrapper for CI monitoring
|
|
372
|
+
├── support/ # Web dashboard, planner, rollback, git tools
|
|
373
|
+
├── locale/ # i18n (en/ko) with prompt templates
|
|
374
|
+
└── __tests__/ # Vitest test suite
|
|
488
375
|
```
|
|
489
376
|
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
| 모듈 | 내보내는 것 |
|
|
493
|
-
|------|------------|
|
|
494
|
-
| 고수준 | `run()`, `inferProviderFromModel()` |
|
|
495
|
-
| 중간 수준 | `spawnCli()` |
|
|
496
|
-
| 어댑터 | `ClaudeCliAdapter`, `CodexCliAdapter`, `getAdapter()`, `setDefaultAdapter()` |
|
|
497
|
-
| 스트림 파서 | `parseCliStreamChunk()`, `extractResultFromStreamJson()` |
|
|
498
|
-
| 비용 파서 | `extractCostFromStreamJson()`, `aggregateCosts()`, `formatCost()` |
|
|
499
|
-
| 버퍼 | `SmartStreamBuffer` |
|
|
377
|
+
## State & Data
|
|
500
378
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
379
|
+
| Path | Description |
|
|
380
|
+
|------|-------------|
|
|
381
|
+
| `~/.openswarm/` | State directory (memory, codex, metrics, workflows) |
|
|
382
|
+
| `~/.openswarm/registry.db` | Code entity registry (SQLite) |
|
|
383
|
+
| `~/.openswarm/issues.db` | Local issue tracker (SQLite) |
|
|
384
|
+
| `~/.claude/openswarm-*.json` | Pipeline history and task state |
|
|
385
|
+
| `config.yaml` | Main configuration |
|
|
386
|
+
| `dist/` | Compiled output |
|
|
505
387
|
|
|
506
388
|
---
|
|
507
389
|
|
|
390
|
+
|
|
508
391
|
## Tech Stack
|
|
509
392
|
|
|
510
393
|
| Category | Technology |
|
|
@@ -512,7 +395,8 @@ import {
|
|
|
512
395
|
| Runtime | Node.js 22+ (ESM) |
|
|
513
396
|
| Language | TypeScript (strict mode) |
|
|
514
397
|
| Build | tsc |
|
|
515
|
-
| Agent Execution | Claude Code
|
|
398
|
+
| Agent Execution | Claude Code, OpenAI GPT/Codex, Ollama/LMStudio/llama.cpp |
|
|
399
|
+
| Local DB | better-sqlite3 (WAL mode, FTS5) |
|
|
516
400
|
| Task Management | Linear SDK (`@linear/sdk`) |
|
|
517
401
|
| Communication | Discord.js 14 |
|
|
518
402
|
| Vector DB | LanceDB + Apache Arrow |
|
|
@@ -522,22 +406,43 @@ import {
|
|
|
522
406
|
| Linting | oxlint |
|
|
523
407
|
| Testing | Vitest |
|
|
524
408
|
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
| Path | Description |
|
|
528
|
-
|------|-------------|
|
|
529
|
-
| `~/.openswarm/` | State directory (memory, codex, metrics, workflows, etc.) |
|
|
530
|
-
| `~/.claude/openswarm-*.json` | Pipeline history and task state |
|
|
531
|
-
| `config.yaml` | Main configuration |
|
|
532
|
-
| `dist/` | Compiled output |
|
|
533
|
-
|
|
534
|
-
## Docker
|
|
409
|
+
---
|
|
535
410
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
411
|
+
## Changelog
|
|
412
|
+
|
|
413
|
+
### v0.3.0
|
|
414
|
+
- **Code Registry**: `openswarm check --scan` scans repo, registers 1000+ entities across 8 languages (TS, Python, Go, Rust, Java, C, C++, C#) with test mapping, complexity scoring, and risk assessment
|
|
415
|
+
- **BS Detector**: `openswarm check --bs` — built-in static analysis for bad code patterns, pipeline guard integration
|
|
416
|
+
- **Local Model Support**: Ollama, LMStudio, llama.cpp via single `local` adapter with auto-detection
|
|
417
|
+
- **GPT Adapter**: OpenAI models via OAuth PKCE flow
|
|
418
|
+
- **Local Issue Tracker**: SQLite + GraphQL + Kanban web UI at `:3847/issues`
|
|
419
|
+
- **CLI**: `openswarm check`, `openswarm annotate` commands
|
|
420
|
+
|
|
421
|
+
### v0.2.2
|
|
422
|
+
- `openswarm` without arguments now launches TUI chat directly
|
|
423
|
+
|
|
424
|
+
### v0.2.1
|
|
425
|
+
- Security: patched lodash, picomatch, rollup, undici, yaml vulnerabilities
|
|
426
|
+
|
|
427
|
+
### v0.2.0
|
|
428
|
+
- Published as `@intrect/openswarm` on npm
|
|
429
|
+
- Extracted `@intrect/claude-driver` as standalone zero-dependency package
|
|
430
|
+
- Autonomous runner hardening and multi-project orchestration
|
|
431
|
+
- Task-state rehydration from Linear comments
|
|
432
|
+
- `--verbose` flag for detailed execution logging
|
|
433
|
+
- Codex adapter: dropped o-series model override
|
|
434
|
+
|
|
435
|
+
### v0.1.0
|
|
436
|
+
- Initial release
|
|
437
|
+
- Worker/Reviewer pair pipeline
|
|
438
|
+
- Claude Code CLI + Codex CLI adapters
|
|
439
|
+
- Discord bot control
|
|
440
|
+
- Linear integration
|
|
441
|
+
- LanceDB cognitive memory
|
|
442
|
+
- Web dashboard (port 3847)
|
|
443
|
+
- Rich TUI chat interface
|
|
539
444
|
|
|
540
|
-
|
|
445
|
+
---
|
|
541
446
|
|
|
542
447
|
## License
|
|
543
448
|
|