glink-engine 0.3.0__tar.gz
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.
- glink_engine-0.3.0/.gitignore +12 -0
- glink_engine-0.3.0/LICENSE +21 -0
- glink_engine-0.3.0/PKG-INFO +229 -0
- glink_engine-0.3.0/README.md +218 -0
- glink_engine-0.3.0/bus/__init__.py +12 -0
- glink_engine-0.3.0/bus/agent_client.py +148 -0
- glink_engine-0.3.0/bus/docs/README.md +67 -0
- glink_engine-0.3.0/bus/main_bus.py +178 -0
- glink_engine-0.3.0/bus/projects/sandbox-builder.jsonl +24 -0
- glink_engine-0.3.0/bus/projects/sandbox-builder.jsonl.bak.20260524_233512 +21 -0
- glink_engine-0.3.0/bus/projects/testglink-analysis.md +165 -0
- glink_engine-0.3.0/bus/projects/testglink-frontend-task.md +23 -0
- glink_engine-0.3.0/bus/projects/testglink.jsonl +48 -0
- glink_engine-0.3.0/bus/tests/TEST_REPORT_2026-05-24.md +89 -0
- glink_engine-0.3.0/daemon/__init__.py +15 -0
- glink_engine-0.3.0/daemon/api.py +424 -0
- glink_engine-0.3.0/daemon/checks.py +118 -0
- glink_engine-0.3.0/daemon/config.py +98 -0
- glink_engine-0.3.0/daemon/core.py +1356 -0
- glink_engine-0.3.0/daemon/log.py +77 -0
- glink_engine-0.3.0/glink-config.yaml +53 -0
- glink_engine-0.3.0/glink-daemon.py +342 -0
- glink_engine-0.3.0/glink.py +155 -0
- glink_engine-0.3.0/pyproject.toml +55 -0
- glink_engine-0.3.0/reporter/reporter.py +313 -0
- glink_engine-0.3.0/workflows/calculator.yaml +59 -0
- glink_engine-0.3.0/workflows/sandbox-builder.yaml +193 -0
- glink_engine-0.3.0/workflows/test-parallel.yaml +23 -0
- glink_engine-0.3.0/workflows/testglink.yaml +131 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Gary Lin
|
|
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.
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: glink-engine
|
|
3
|
+
Version: 0.3.0
|
|
4
|
+
Summary: Multi-Agent Workflow Orchestration Engine. One Bus. Zero Friction.
|
|
5
|
+
Author: Gary Lin
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Keywords: agent,ai,automation,orchestration,pipeline,workflow
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# Glink
|
|
13
|
+
|
|
14
|
+
> **Multi-Agent Workflow Orchestration. One Bus. Zero Friction.**
|
|
15
|
+
|
|
16
|
+
Glink is a lightweight orchestration engine that turns your AI agents into a **collaborative assembly line**. Define a workflow in YAML, and Glink routes each step to the right agent — passing context, handling failures, and logging every heartbeat onto a shared event bus.
|
|
17
|
+
|
|
18
|
+
No database. No message queue. No external dependencies.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Architecture
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
┌──────────────────────────────────────────────────────────┐
|
|
26
|
+
│ Glink Daemon │
|
|
27
|
+
│ │
|
|
28
|
+
│ ┌─────────────────────────────────────────────────────┐ │
|
|
29
|
+
│ │ Main Bus │ │
|
|
30
|
+
│ │ Append-only JSONL Timeline │ │
|
|
31
|
+
│ └──────┬──────────┬──────────┬──────────┬─────────────┘ │
|
|
32
|
+
│ │ │ │ │ │
|
|
33
|
+
│ ┌────▼───┐ ┌───▼────┐ ┌───▼────┐ ┌──▼──────┐ │
|
|
34
|
+
│ │Agent-1 │ │Agent-2 │ │Agent-3 │ │... │ │
|
|
35
|
+
│ │:8420 │ │:8431 │ │:8432 │ │ │ │
|
|
36
|
+
│ └────────┘ └────────┘ └────────┘ └─────────┘ │
|
|
37
|
+
│ │ │ │ │ │
|
|
38
|
+
│ └──────────┴──────────┴──────────┘ │
|
|
39
|
+
│ Your AI Fleet │
|
|
40
|
+
└──────────────────────────────────────────────────────────┘
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## Quick Start
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Define your agents in main.py or daemon/core.py
|
|
49
|
+
# Default port mapping (customize freely):
|
|
50
|
+
# agent-1 :8420 (generalist)
|
|
51
|
+
# agent-2 :8431 (backend)
|
|
52
|
+
# agent-3 :8432 (frontend/UI)
|
|
53
|
+
# agent-4 :8434 (data)
|
|
54
|
+
# agent-5 :8435 (testing)
|
|
55
|
+
|
|
56
|
+
# Run a workflow (auto-resumes from last checkpoint)
|
|
57
|
+
python3 glink-daemon.py my-workflow
|
|
58
|
+
|
|
59
|
+
# Force restart from step 1
|
|
60
|
+
python3 glink-daemon.py my-workflow --force
|
|
61
|
+
|
|
62
|
+
# Jump to a specific step
|
|
63
|
+
python3 glink-daemon.py my-workflow --step 4
|
|
64
|
+
|
|
65
|
+
# Serve-only mode (API daemon without running workflow)
|
|
66
|
+
python3 glink-daemon.py --serve
|
|
67
|
+
|
|
68
|
+
# Open dashboard
|
|
69
|
+
open http://127.0.0.1:8426/commander.html
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## Features
|
|
75
|
+
|
|
76
|
+
| Feature | Description |
|
|
77
|
+
|:--------|:------------|
|
|
78
|
+
| **YAML Workflows** | Define steps, agents, dependencies, and fallbacks in one file |
|
|
79
|
+
| **Main Bus** | JSONL blackboard — append-only, agent-agnostic, replayable |
|
|
80
|
+
| **Smart Routing** | Primary agent down? Auto-fallback to the next in line |
|
|
81
|
+
| **Checkpoint Resume** | Crash mid-workflow? Restart picks up where it left off |
|
|
82
|
+
| **Dependency Graph** | Steps can `depends_on` each other; Glink handles ordering |
|
|
83
|
+
| **Retry Loop** | Auto-retry failed steps (configurable, default 2×) |
|
|
84
|
+
| **HTTP API + SSE** | Live status, agent health, and event stream on `:8426` |
|
|
85
|
+
| **Self-Healing** | Daemon auto-restarts on crash, PID-based watchdog |
|
|
86
|
+
| **Webhook Alerts** | Push notifications to any HTTP endpoint |
|
|
87
|
+
| **Zero External Deps** | Pure Python 3.10+, standard library. No pip install. |
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Define a Workflow
|
|
92
|
+
|
|
93
|
+
```yaml
|
|
94
|
+
name: my-pipeline
|
|
95
|
+
version: 1.0.0
|
|
96
|
+
description: "A simple 3-step demo"
|
|
97
|
+
|
|
98
|
+
global_context: |
|
|
99
|
+
You are part of a multi-agent orchestration pipeline.
|
|
100
|
+
Build upon the output of previous steps.
|
|
101
|
+
|
|
102
|
+
steps:
|
|
103
|
+
- id: step-1
|
|
104
|
+
executor: agent-1
|
|
105
|
+
title: "Generate content"
|
|
106
|
+
output_file: projects/demo/step1.txt
|
|
107
|
+
task: |
|
|
108
|
+
Create a summary of what makes a good multi-agent workflow.
|
|
109
|
+
|
|
110
|
+
- id: step-2
|
|
111
|
+
executor: agent-2
|
|
112
|
+
title: "Enhance"
|
|
113
|
+
input_file: projects/demo/step1.txt
|
|
114
|
+
output_file: projects/demo/step2.md
|
|
115
|
+
task: |
|
|
116
|
+
Read and enhance the step-1 output. Add code examples.
|
|
117
|
+
|
|
118
|
+
- id: step-3
|
|
119
|
+
executor: agent-3
|
|
120
|
+
title: "Verify"
|
|
121
|
+
input_file: projects/demo/step2.md
|
|
122
|
+
output_file: projects/demo/VERIFIED.md
|
|
123
|
+
task: |
|
|
124
|
+
Verify the enhanced document is complete.
|
|
125
|
+
Append a verification seal if all checks pass.
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## API Reference
|
|
131
|
+
|
|
132
|
+
All endpoints served on the configured port (`:8426` by default).
|
|
133
|
+
|
|
134
|
+
| Method | Endpoint | Description |
|
|
135
|
+
|:-------|:---------|:------------|
|
|
136
|
+
| `GET` | `/health` | Liveness check |
|
|
137
|
+
| `GET` | `/status` | Full project status + step-by-step progress |
|
|
138
|
+
| `GET` | `/status/agents` | Which agents are online |
|
|
139
|
+
| `GET` | `/status/events?n=20` | Last N bus events |
|
|
140
|
+
| `GET` | `/intel/step` | Detailed intelligence per step stage |
|
|
141
|
+
| `GET` | `/intel/agents` | Agent-specific metrics |
|
|
142
|
+
| `GET` | `/intel/timeline` | Step timeline visualization data |
|
|
143
|
+
| `GET` | `/events/stream` | SSE real-time event stream |
|
|
144
|
+
| `POST` | `/restart` | Resume from last checkpoint |
|
|
145
|
+
| `POST` | `/restart?force` | Force restart from step 1 |
|
|
146
|
+
| `POST` | `/restart?step=N` | Jump to specific step |
|
|
147
|
+
|
|
148
|
+
---
|
|
149
|
+
|
|
150
|
+
## Configuration
|
|
151
|
+
|
|
152
|
+
See `glink-config.yaml`:
|
|
153
|
+
|
|
154
|
+
```yaml
|
|
155
|
+
project:
|
|
156
|
+
default: hello-world
|
|
157
|
+
|
|
158
|
+
scheduling:
|
|
159
|
+
max_retries: 2
|
|
160
|
+
poll_interval: 3
|
|
161
|
+
poll_max_wait: 180
|
|
162
|
+
max_concurrent_steps: 1
|
|
163
|
+
|
|
164
|
+
reporting:
|
|
165
|
+
channels:
|
|
166
|
+
- type: console
|
|
167
|
+
label: "Glink"
|
|
168
|
+
# - type: webhook
|
|
169
|
+
# url: "https://hooks.example.com/..."
|
|
170
|
+
# label: "Slack"
|
|
171
|
+
|
|
172
|
+
server:
|
|
173
|
+
host: "127.0.0.1"
|
|
174
|
+
port: 8426
|
|
175
|
+
|
|
176
|
+
security:
|
|
177
|
+
startup_timeout: 10
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Environment variables:
|
|
181
|
+
- `GLINK_DEFAULT_PROJECT` — override default project name
|
|
182
|
+
- `GLINK_PORT` — override API server port
|
|
183
|
+
- `GLINK_REPORTER` — set to `webhook`, `console`, or `silent`
|
|
184
|
+
- `GLINK_ALERT_WEBHOOK` — webhook URL for alerts
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Real-World Usage
|
|
189
|
+
|
|
190
|
+
Glink was used to orchestrate a **10-step game development pipeline** across 5 agents:
|
|
191
|
+
|
|
192
|
+
- Step 1-4: 3D scene, physics, textures, UI
|
|
193
|
+
- Step 5-6: Game systems (save/load, scoring)
|
|
194
|
+
- Step 7-8: Quality verification
|
|
195
|
+
- Result: Single playable HTML file, 97 KB / 2,751 lines
|
|
196
|
+
|
|
197
|
+
All built by agent collaboration — zero lines of human-written code.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Project Structure
|
|
202
|
+
|
|
203
|
+
```
|
|
204
|
+
glink/
|
|
205
|
+
├── glink-daemon.py # CLI entry point
|
|
206
|
+
├── glink-config.yaml # Configuration
|
|
207
|
+
├── daemon/
|
|
208
|
+
│ ├── core.py # Workflow orchestration engine
|
|
209
|
+
│ ├── api.py # HTTP API server (17 endpoints)
|
|
210
|
+
│ ├── checks.py # PID management & auto-recovery
|
|
211
|
+
│ ├── config.py # Config loader
|
|
212
|
+
│ └── log.py # Reporter initialization
|
|
213
|
+
├── bus/
|
|
214
|
+
│ ├── main_bus.py # JSONL event bus
|
|
215
|
+
│ └── agent_client.py # Agent HTTP client
|
|
216
|
+
├── reporter/
|
|
217
|
+
│ └── reporter.py # Notification session (webhook/console)
|
|
218
|
+
├── dashboard/
|
|
219
|
+
│ ├── commander.html # C2 dashboard (realtime)
|
|
220
|
+
│ └── index.html # Legacy dashboard
|
|
221
|
+
├── workflows/ # Your YAML workflow definitions
|
|
222
|
+
└── projects/ # Step outputs by project
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## License
|
|
228
|
+
|
|
229
|
+
MIT — free for any use, open or commercial.
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# Glink
|
|
2
|
+
|
|
3
|
+
> **Multi-Agent Workflow Orchestration. One Bus. Zero Friction.**
|
|
4
|
+
|
|
5
|
+
Glink is a lightweight orchestration engine that turns your AI agents into a **collaborative assembly line**. Define a workflow in YAML, and Glink routes each step to the right agent — passing context, handling failures, and logging every heartbeat onto a shared event bus.
|
|
6
|
+
|
|
7
|
+
No database. No message queue. No external dependencies.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Architecture
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
┌──────────────────────────────────────────────────────────┐
|
|
15
|
+
│ Glink Daemon │
|
|
16
|
+
│ │
|
|
17
|
+
│ ┌─────────────────────────────────────────────────────┐ │
|
|
18
|
+
│ │ Main Bus │ │
|
|
19
|
+
│ │ Append-only JSONL Timeline │ │
|
|
20
|
+
│ └──────┬──────────┬──────────┬──────────┬─────────────┘ │
|
|
21
|
+
│ │ │ │ │ │
|
|
22
|
+
│ ┌────▼───┐ ┌───▼────┐ ┌───▼────┐ ┌──▼──────┐ │
|
|
23
|
+
│ │Agent-1 │ │Agent-2 │ │Agent-3 │ │... │ │
|
|
24
|
+
│ │:8420 │ │:8431 │ │:8432 │ │ │ │
|
|
25
|
+
│ └────────┘ └────────┘ └────────┘ └─────────┘ │
|
|
26
|
+
│ │ │ │ │ │
|
|
27
|
+
│ └──────────┴──────────┴──────────┘ │
|
|
28
|
+
│ Your AI Fleet │
|
|
29
|
+
└──────────────────────────────────────────────────────────┘
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Quick Start
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
# Define your agents in main.py or daemon/core.py
|
|
38
|
+
# Default port mapping (customize freely):
|
|
39
|
+
# agent-1 :8420 (generalist)
|
|
40
|
+
# agent-2 :8431 (backend)
|
|
41
|
+
# agent-3 :8432 (frontend/UI)
|
|
42
|
+
# agent-4 :8434 (data)
|
|
43
|
+
# agent-5 :8435 (testing)
|
|
44
|
+
|
|
45
|
+
# Run a workflow (auto-resumes from last checkpoint)
|
|
46
|
+
python3 glink-daemon.py my-workflow
|
|
47
|
+
|
|
48
|
+
# Force restart from step 1
|
|
49
|
+
python3 glink-daemon.py my-workflow --force
|
|
50
|
+
|
|
51
|
+
# Jump to a specific step
|
|
52
|
+
python3 glink-daemon.py my-workflow --step 4
|
|
53
|
+
|
|
54
|
+
# Serve-only mode (API daemon without running workflow)
|
|
55
|
+
python3 glink-daemon.py --serve
|
|
56
|
+
|
|
57
|
+
# Open dashboard
|
|
58
|
+
open http://127.0.0.1:8426/commander.html
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Features
|
|
64
|
+
|
|
65
|
+
| Feature | Description |
|
|
66
|
+
|:--------|:------------|
|
|
67
|
+
| **YAML Workflows** | Define steps, agents, dependencies, and fallbacks in one file |
|
|
68
|
+
| **Main Bus** | JSONL blackboard — append-only, agent-agnostic, replayable |
|
|
69
|
+
| **Smart Routing** | Primary agent down? Auto-fallback to the next in line |
|
|
70
|
+
| **Checkpoint Resume** | Crash mid-workflow? Restart picks up where it left off |
|
|
71
|
+
| **Dependency Graph** | Steps can `depends_on` each other; Glink handles ordering |
|
|
72
|
+
| **Retry Loop** | Auto-retry failed steps (configurable, default 2×) |
|
|
73
|
+
| **HTTP API + SSE** | Live status, agent health, and event stream on `:8426` |
|
|
74
|
+
| **Self-Healing** | Daemon auto-restarts on crash, PID-based watchdog |
|
|
75
|
+
| **Webhook Alerts** | Push notifications to any HTTP endpoint |
|
|
76
|
+
| **Zero External Deps** | Pure Python 3.10+, standard library. No pip install. |
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
## Define a Workflow
|
|
81
|
+
|
|
82
|
+
```yaml
|
|
83
|
+
name: my-pipeline
|
|
84
|
+
version: 1.0.0
|
|
85
|
+
description: "A simple 3-step demo"
|
|
86
|
+
|
|
87
|
+
global_context: |
|
|
88
|
+
You are part of a multi-agent orchestration pipeline.
|
|
89
|
+
Build upon the output of previous steps.
|
|
90
|
+
|
|
91
|
+
steps:
|
|
92
|
+
- id: step-1
|
|
93
|
+
executor: agent-1
|
|
94
|
+
title: "Generate content"
|
|
95
|
+
output_file: projects/demo/step1.txt
|
|
96
|
+
task: |
|
|
97
|
+
Create a summary of what makes a good multi-agent workflow.
|
|
98
|
+
|
|
99
|
+
- id: step-2
|
|
100
|
+
executor: agent-2
|
|
101
|
+
title: "Enhance"
|
|
102
|
+
input_file: projects/demo/step1.txt
|
|
103
|
+
output_file: projects/demo/step2.md
|
|
104
|
+
task: |
|
|
105
|
+
Read and enhance the step-1 output. Add code examples.
|
|
106
|
+
|
|
107
|
+
- id: step-3
|
|
108
|
+
executor: agent-3
|
|
109
|
+
title: "Verify"
|
|
110
|
+
input_file: projects/demo/step2.md
|
|
111
|
+
output_file: projects/demo/VERIFIED.md
|
|
112
|
+
task: |
|
|
113
|
+
Verify the enhanced document is complete.
|
|
114
|
+
Append a verification seal if all checks pass.
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
## API Reference
|
|
120
|
+
|
|
121
|
+
All endpoints served on the configured port (`:8426` by default).
|
|
122
|
+
|
|
123
|
+
| Method | Endpoint | Description |
|
|
124
|
+
|:-------|:---------|:------------|
|
|
125
|
+
| `GET` | `/health` | Liveness check |
|
|
126
|
+
| `GET` | `/status` | Full project status + step-by-step progress |
|
|
127
|
+
| `GET` | `/status/agents` | Which agents are online |
|
|
128
|
+
| `GET` | `/status/events?n=20` | Last N bus events |
|
|
129
|
+
| `GET` | `/intel/step` | Detailed intelligence per step stage |
|
|
130
|
+
| `GET` | `/intel/agents` | Agent-specific metrics |
|
|
131
|
+
| `GET` | `/intel/timeline` | Step timeline visualization data |
|
|
132
|
+
| `GET` | `/events/stream` | SSE real-time event stream |
|
|
133
|
+
| `POST` | `/restart` | Resume from last checkpoint |
|
|
134
|
+
| `POST` | `/restart?force` | Force restart from step 1 |
|
|
135
|
+
| `POST` | `/restart?step=N` | Jump to specific step |
|
|
136
|
+
|
|
137
|
+
---
|
|
138
|
+
|
|
139
|
+
## Configuration
|
|
140
|
+
|
|
141
|
+
See `glink-config.yaml`:
|
|
142
|
+
|
|
143
|
+
```yaml
|
|
144
|
+
project:
|
|
145
|
+
default: hello-world
|
|
146
|
+
|
|
147
|
+
scheduling:
|
|
148
|
+
max_retries: 2
|
|
149
|
+
poll_interval: 3
|
|
150
|
+
poll_max_wait: 180
|
|
151
|
+
max_concurrent_steps: 1
|
|
152
|
+
|
|
153
|
+
reporting:
|
|
154
|
+
channels:
|
|
155
|
+
- type: console
|
|
156
|
+
label: "Glink"
|
|
157
|
+
# - type: webhook
|
|
158
|
+
# url: "https://hooks.example.com/..."
|
|
159
|
+
# label: "Slack"
|
|
160
|
+
|
|
161
|
+
server:
|
|
162
|
+
host: "127.0.0.1"
|
|
163
|
+
port: 8426
|
|
164
|
+
|
|
165
|
+
security:
|
|
166
|
+
startup_timeout: 10
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Environment variables:
|
|
170
|
+
- `GLINK_DEFAULT_PROJECT` — override default project name
|
|
171
|
+
- `GLINK_PORT` — override API server port
|
|
172
|
+
- `GLINK_REPORTER` — set to `webhook`, `console`, or `silent`
|
|
173
|
+
- `GLINK_ALERT_WEBHOOK` — webhook URL for alerts
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
## Real-World Usage
|
|
178
|
+
|
|
179
|
+
Glink was used to orchestrate a **10-step game development pipeline** across 5 agents:
|
|
180
|
+
|
|
181
|
+
- Step 1-4: 3D scene, physics, textures, UI
|
|
182
|
+
- Step 5-6: Game systems (save/load, scoring)
|
|
183
|
+
- Step 7-8: Quality verification
|
|
184
|
+
- Result: Single playable HTML file, 97 KB / 2,751 lines
|
|
185
|
+
|
|
186
|
+
All built by agent collaboration — zero lines of human-written code.
|
|
187
|
+
|
|
188
|
+
---
|
|
189
|
+
|
|
190
|
+
## Project Structure
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
glink/
|
|
194
|
+
├── glink-daemon.py # CLI entry point
|
|
195
|
+
├── glink-config.yaml # Configuration
|
|
196
|
+
├── daemon/
|
|
197
|
+
│ ├── core.py # Workflow orchestration engine
|
|
198
|
+
│ ├── api.py # HTTP API server (17 endpoints)
|
|
199
|
+
│ ├── checks.py # PID management & auto-recovery
|
|
200
|
+
│ ├── config.py # Config loader
|
|
201
|
+
│ └── log.py # Reporter initialization
|
|
202
|
+
├── bus/
|
|
203
|
+
│ ├── main_bus.py # JSONL event bus
|
|
204
|
+
│ └── agent_client.py # Agent HTTP client
|
|
205
|
+
├── reporter/
|
|
206
|
+
│ └── reporter.py # Notification session (webhook/console)
|
|
207
|
+
├── dashboard/
|
|
208
|
+
│ ├── commander.html # C2 dashboard (realtime)
|
|
209
|
+
│ └── index.html # Legacy dashboard
|
|
210
|
+
├── workflows/ # Your YAML workflow definitions
|
|
211
|
+
└── projects/ # Step outputs by project
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## License
|
|
217
|
+
|
|
218
|
+
MIT — free for any use, open or commercial.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
|
2
|
+
"""Glink Bus 包 — 共享工具与总线模块"""
|
|
3
|
+
|
|
4
|
+
import re
|
|
5
|
+
|
|
6
|
+
# ── 项目名白名单:仅允许字母、数字、下划线、连字符(防 path traversal)──
|
|
7
|
+
_PROJECT_NAME_RE = re.compile(r"[^\w\-]")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def sanitize_project_name(project_name: str) -> str:
|
|
11
|
+
"""过滤项目名,防止 path traversal(仅保留 [\\w\\-])"""
|
|
12
|
+
return _PROJECT_NAME_RE.sub("", project_name or "")
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
agent_client — Glink 共享的 Agent 通讯与工作流加载模块
|
|
4
|
+
|
|
5
|
+
由 glink.py(一次性调度引擎)和 glink-daemon.py(带断点续跑的守护进程)共享。
|
|
6
|
+
|
|
7
|
+
导出:
|
|
8
|
+
- AGENT_PORTS: Agent 名称 → HTTP 端口的统一映射
|
|
9
|
+
- call_agent(): HTTP 调用 Agent 的 /ask 接口
|
|
10
|
+
- load_workflow(): 从 workflows/ 或 bus/projects/ 加载 yaml 工作流
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import json
|
|
16
|
+
import os
|
|
17
|
+
import sys
|
|
18
|
+
import urllib.error
|
|
19
|
+
import urllib.request
|
|
20
|
+
from typing import Any
|
|
21
|
+
|
|
22
|
+
import yaml
|
|
23
|
+
|
|
24
|
+
# ── Agent 端口映射(唯一真源)────────────────────────────
|
|
25
|
+
# 同一端口可有多个别名(如 标准版/扎古、代码臂/Forge/forge)
|
|
26
|
+
AGENT_PORTS: dict[str, int] = {
|
|
27
|
+
"标准版": 8420,
|
|
28
|
+
"扎古": 8420,
|
|
29
|
+
"重锤": 8431,
|
|
30
|
+
"绘墨": 8432,
|
|
31
|
+
"大黄蜂": 8434,
|
|
32
|
+
"Laser": 8435,
|
|
33
|
+
"代码臂": 8436,
|
|
34
|
+
"Forge": 8436,
|
|
35
|
+
"forge": 8436,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
DEFAULT_AGENT_PORT = 8420
|
|
39
|
+
DEFAULT_TIMEOUT = 600
|
|
40
|
+
|
|
41
|
+
# ── 项目名白名单(防 path traversal,从 bus/__init__.py 导入)──
|
|
42
|
+
from . import sanitize_project_name
|
|
43
|
+
|
|
44
|
+
_sanitize_project_name = sanitize_project_name # 兼容别名
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# ── HTTP 调用 Agent ─────────────────────────────────────
|
|
48
|
+
def call_agent(
|
|
49
|
+
agent: str,
|
|
50
|
+
task: str,
|
|
51
|
+
port: int | None = None,
|
|
52
|
+
timeout: int = DEFAULT_TIMEOUT,
|
|
53
|
+
parse_reply: bool = True,
|
|
54
|
+
) -> dict[str, Any]:
|
|
55
|
+
"""HTTP 调用 agent 的 /ask 接口。
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
agent: Agent 名称(如 "重锤"、"Forge")
|
|
59
|
+
task: 发送给 Agent 的任务描述
|
|
60
|
+
port: 显式端口;不传则查 AGENT_PORTS
|
|
61
|
+
timeout: 请求超时秒数
|
|
62
|
+
parse_reply: True=尝试解析 JSON 取 reply 字段;False=直接返回原始响应
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
{"status": "ok", "output": "<reply 或原始响应前500字>"}
|
|
66
|
+
{"status": "failed", "error": "<错误描述>"}
|
|
67
|
+
"""
|
|
68
|
+
if port is None:
|
|
69
|
+
port = AGENT_PORTS.get(agent, DEFAULT_AGENT_PORT)
|
|
70
|
+
|
|
71
|
+
url = f"http://127.0.0.1:{port}/ask"
|
|
72
|
+
payload = json.dumps({"message": task, "session": True}).encode()
|
|
73
|
+
req = urllib.request.Request(url, data=payload, headers={"Content-Type": "application/json"})
|
|
74
|
+
|
|
75
|
+
max_response_size = 1 * 1024 * 1024 # 1 MB
|
|
76
|
+
chunk_size = 64 * 1024 # 64 KB
|
|
77
|
+
|
|
78
|
+
try:
|
|
79
|
+
with urllib.request.urlopen(req, timeout=timeout) as resp:
|
|
80
|
+
chunks = []
|
|
81
|
+
total = 0
|
|
82
|
+
while True:
|
|
83
|
+
chunk = resp.read(chunk_size)
|
|
84
|
+
if not chunk:
|
|
85
|
+
break
|
|
86
|
+
total += len(chunk)
|
|
87
|
+
if total > max_response_size:
|
|
88
|
+
max_read = max_response_size - (total - len(chunk))
|
|
89
|
+
if max_read > 0:
|
|
90
|
+
chunks.append(chunk[:max_read])
|
|
91
|
+
while resp.read(chunk_size):
|
|
92
|
+
pass
|
|
93
|
+
body = b"".join(chunks).decode()
|
|
94
|
+
body = body[:max_response_size] + "\n\n[TRUNCATED: Response exceeded 1MB limit]"
|
|
95
|
+
break
|
|
96
|
+
chunks.append(chunk)
|
|
97
|
+
else:
|
|
98
|
+
body = b"".join(chunks).decode()
|
|
99
|
+
|
|
100
|
+
if parse_reply:
|
|
101
|
+
try:
|
|
102
|
+
output = json.loads(body).get("reply", body[:500])
|
|
103
|
+
except json.JSONDecodeError:
|
|
104
|
+
output = body[:500]
|
|
105
|
+
else:
|
|
106
|
+
output = body[:500]
|
|
107
|
+
return {"status": "ok", "output": output}
|
|
108
|
+
except urllib.error.HTTPError as e:
|
|
109
|
+
body = e.read().decode()[:200]
|
|
110
|
+
return {"status": "failed", "error": f"HTTP {e.code}: {body}"}
|
|
111
|
+
except Exception as e:
|
|
112
|
+
return {"status": "failed", "error": str(e)}
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# ── 工作流加载 ───────────────────────────────────────────
|
|
116
|
+
def load_workflow(project_name: str, base_dir: str | None = None) -> dict[str, Any]:
|
|
117
|
+
"""加载工作流 YAML,先查 workflows/,再查 bus/projects/。
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
project_name: 项目名(会被白名单过滤)
|
|
121
|
+
base_dir: Glink 根目录;不传则用本文件所在目录的父级
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
解析后的工作流字典
|
|
125
|
+
|
|
126
|
+
Raises:
|
|
127
|
+
SystemExit(1): 找不到工作流文件
|
|
128
|
+
"""
|
|
129
|
+
if base_dir is None:
|
|
130
|
+
# 本文件位于 <glink>/bus/agent_client.py,父级 = glink 根
|
|
131
|
+
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
132
|
+
|
|
133
|
+
workflows_dir = os.path.join(base_dir, "workflows")
|
|
134
|
+
bus_projects_dir = os.path.join(base_dir, "bus", "projects")
|
|
135
|
+
|
|
136
|
+
safe_name = _sanitize_project_name(project_name)
|
|
137
|
+
candidates = [
|
|
138
|
+
os.path.join(workflows_dir, f"{safe_name}.yaml"),
|
|
139
|
+
os.path.join(bus_projects_dir, f"{safe_name}.yaml"),
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
for path in candidates:
|
|
143
|
+
if os.path.exists(path):
|
|
144
|
+
with open(path, encoding="utf-8") as f:
|
|
145
|
+
return yaml.safe_load(f)
|
|
146
|
+
|
|
147
|
+
print(f"❌ 找不到工作流: {safe_name}", file=sys.stderr)
|
|
148
|
+
sys.exit(1)
|