agi-farm 1.1.1 โ 1.2.2
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 +40 -32
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -6
- package/dist/index.js.map +1 -1
- package/openclaw.plugin.json +8 -8
- package/package.json +3 -1
- package/scripts/dashboard.js +0 -0
- package/scripts/dispatch.js +177 -34
- package/scripts/export.js +34 -21
- package/scripts/rebuild.js +131 -39
- package/scripts/setup.js +48 -25
- package/scripts/status.js +36 -17
- package/scripts/teardown.js +87 -22
- package/scripts/test-install-smoke.sh +65 -0
- package/server/dashboard.js +18 -4
package/README.md
CHANGED
|
@@ -36,33 +36,19 @@
|
|
|
36
36
|
|
|
37
37
|
---
|
|
38
38
|
|
|
39
|
-
## ๐ What's New in v1.
|
|
40
|
-
|
|
41
|
-
###
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
-
|
|
45
|
-
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
|
|
53
|
-
### Interactive Dashboard
|
|
54
|
-
- โ **Task Creation** โ create tasks directly from the Tasks tab with assignee, priority, and dependencies
|
|
55
|
-
- ๐ฌ **Agent Messaging** โ send messages to individual agents from their cards
|
|
56
|
-
- ๐ **Broadcast Compose** โ post team-wide announcements from the Broadcast tab
|
|
57
|
-
- ๐ **Knowledge CRUD** โ add and remove knowledge entries from the dashboard
|
|
58
|
-
- ๐ **Search & Sort** โ filter tasks and agents with real-time search; sortable task columns
|
|
59
|
-
|
|
60
|
-
### Security & Infrastructure
|
|
61
|
-
- ๐ **CSRF + Origin hardening**: same-origin validation, timing-safe auth, CSP headers
|
|
62
|
-
- ๐ก๏ธ **Input validation**: `isSafeId()`, `sanitizeNote()`, path traversal protection
|
|
63
|
-
- ๐งช **103 automated tests** โ unit, integration, API, updater, data extraction
|
|
64
|
-
- ๐ **Toast notifications** โ non-blocking success/error toasts for all actions
|
|
65
|
-
- ๐ก๏ธ **Atomic file writes** โ `.tmp` โ `rename` pattern with in-memory file locks
|
|
39
|
+
## ๐ What's New in v1.2.0 & v1.2.1
|
|
40
|
+
|
|
41
|
+
### Production Ready & Seamless OpenClaw Integration
|
|
42
|
+
- ๐ **Default Model Support**: Removed all hardcoded model IDs in favor of OpenClaw's intelligent default auto-routing (`manifest/auto`).
|
|
43
|
+
- ๐งน **Robust Cleanup**: Enhanced `agi-farm teardown` ensures complete cleanup of bundles, crons, and communication logs.
|
|
44
|
+
- ๐ **Gateway Health Probe**: Dashboard proactively monitors OpenClaw gateway connection.
|
|
45
|
+
- ๐ **Zero Python Dependencies**: Re-architected for seamless universal installation without Python prerequisites.
|
|
46
|
+
- ๐ **macOS LaunchAgent**: Optional `agi-farm-launchagent` command to install dashboard as a persistent OS-level login service.
|
|
47
|
+
|
|
48
|
+
### Previous Highlights (v1.1.x)
|
|
49
|
+
- ๐ **GitHub release detection & Auto-Update system directly from dashboard**
|
|
50
|
+
- โ๏ธ **Feature-Flagged Core Runtime** (Jobs, Approvals, Memory, Skills)
|
|
51
|
+
- ๐ก๏ธ **Extensive Security Hardening** (CSRF, CSP, token validation, atomic file writes)
|
|
66
52
|
|
|
67
53
|
---
|
|
68
54
|
|
|
@@ -124,11 +110,11 @@
|
|
|
124
110
|
### Install
|
|
125
111
|
|
|
126
112
|
```bash
|
|
127
|
-
#
|
|
128
|
-
npm install -g agi-farm
|
|
129
|
-
|
|
130
|
-
# Or via OpenClaw plugin manager
|
|
113
|
+
# Recommended: install via OpenClaw plugin manager
|
|
131
114
|
openclaw plugins install agi-farm
|
|
115
|
+
|
|
116
|
+
# Optional: install standalone global CLI binary
|
|
117
|
+
npm install -g agi-farm
|
|
132
118
|
```
|
|
133
119
|
|
|
134
120
|
### Run Setup Wizard
|
|
@@ -164,7 +150,7 @@ Answer the setup prompts and your team will be live in ~2 minutes:
|
|
|
164
150
|
| Command | CLI Shortcut | Description |
|
|
165
151
|
|---------|-------------|-------------|
|
|
166
152
|
| ๐ฏ `agi-farm setup` | `agi-farm` | Full wizard โ agents, workspace, crons |
|
|
167
|
-
| ๐๏ธ `agi-farm teardown` | `agi-farm-teardown` |
|
|
153
|
+
| ๐๏ธ `agi-farm teardown` | `agi-farm-teardown` | Team teardown โ removes AGI Farm agents, bundle, and workspace registries |
|
|
168
154
|
| ๐ `agi-farm status` | `agi-farm-status` | Team health: agents, tasks, cron status |
|
|
169
155
|
| ๐ง `agi-farm rebuild` | `agi-farm-rebuild` | Regenerate workspace from bundle |
|
|
170
156
|
| ๐ค `agi-farm export` | `agi-farm-export` | Push bundle to GitHub |
|
|
@@ -554,6 +540,28 @@ npm run build:all
|
|
|
554
540
|
ln -s $(pwd) ~/.openclaw/extensions/agi-farm
|
|
555
541
|
```
|
|
556
542
|
|
|
543
|
+
> If you later switch to `openclaw plugins install agi-farm`, remove the dev symlink first:
|
|
544
|
+
> `rm -f ~/.openclaw/extensions/agi-farm`
|
|
545
|
+
|
|
546
|
+
---
|
|
547
|
+
|
|
548
|
+
## ๐งน Uninstall Guide
|
|
549
|
+
|
|
550
|
+
```bash
|
|
551
|
+
# 1) Remove AGI Farm team data (agents, bundle, registries)
|
|
552
|
+
agi-farm teardown
|
|
553
|
+
|
|
554
|
+
# 2) Remove plugin from OpenClaw
|
|
555
|
+
openclaw plugins uninstall agi-farm --force
|
|
556
|
+
|
|
557
|
+
# 3) Optional: remove standalone global CLI binary
|
|
558
|
+
npm uninstall -g agi-farm
|
|
559
|
+
```
|
|
560
|
+
|
|
561
|
+
Notes:
|
|
562
|
+
- `agi-farm teardown` does not uninstall the plugin package.
|
|
563
|
+
- `openclaw plugins uninstall agi-farm --force` removes the plugin install used by OpenClaw.
|
|
564
|
+
|
|
557
565
|
---
|
|
558
566
|
|
|
559
567
|
## ๐ป Development
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,15 @@ export interface ExtensionContext {
|
|
|
15
15
|
logger: Logger;
|
|
16
16
|
config: Record<string, unknown>;
|
|
17
17
|
}
|
|
18
|
+
export interface PluginContext {
|
|
19
|
+
logger: Logger;
|
|
20
|
+
pluginConfig?: Partial<AGIFarmConfig>;
|
|
21
|
+
registerService?: (service: {
|
|
22
|
+
id: string;
|
|
23
|
+
start: () => Promise<void> | void;
|
|
24
|
+
stop: () => Promise<void> | void;
|
|
25
|
+
}) => void;
|
|
26
|
+
}
|
|
18
27
|
export interface OpenClawExtension {
|
|
19
28
|
id: string;
|
|
20
29
|
name: string;
|
|
@@ -89,5 +98,9 @@ declare class AGIFarmExtension implements OpenClawExtension {
|
|
|
89
98
|
* Extension factory - called by OpenClaw to instantiate the plugin
|
|
90
99
|
*/
|
|
91
100
|
export default function createExtension(config: Partial<AGIFarmConfig>): OpenClawExtension;
|
|
101
|
+
/**
|
|
102
|
+
* OpenClaw plugin registration entrypoint for runtimes that expect `register`.
|
|
103
|
+
*/
|
|
104
|
+
export declare function register(context: PluginContext): void;
|
|
92
105
|
export { AGIFarmExtension };
|
|
93
106
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,sCAAsC;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,0CAA0C;IAC1C,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iDAAiD;IACjD,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iCAAiC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8BAA8B;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oBAAoB;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAqBD;;;;GAIG;AACH,cAAM,gBAAiB,YAAW,iBAAiB;IACjD,EAAE,SAAc;IAChB,IAAI,SAAyC;IAC7C,OAAO,SAAe;IAEtB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,gBAAgB,CAA6B;gBAEzC,MAAM,GAAE,OAAO,CAAC,aAAa,CAAM;IAI/C;;OAEG;IACG,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBtD;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ/B;;OAEG;YACW,cAAc;IA4C5B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAMxB;;OAEG;YACW,eAAe;IA6C7B;;OAEG;IACH,eAAe,IAAI,MAAM;IAIzB;;OAEG;IACH,kBAAkB,IAAI,OAAO;CAG9B;AAED;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,iBAAiB,CAEzF;AAGD,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAaH,MAAM,WAAW,MAAM;IACrB,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACjC;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,YAAY,CAAC,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IACtC,eAAe,CAAC,EAAE,CAAC,OAAO,EAAE;QAC1B,EAAE,EAAE,MAAM,CAAC;QACX,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;QAClC,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;KAClC,KAAK,IAAI,CAAC;CACZ;AAED,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3B;AAED,MAAM,WAAW,aAAa;IAC5B,sCAAsC;IACtC,aAAa,EAAE,MAAM,CAAC;IACtB,oCAAoC;IACpC,aAAa,EAAE,MAAM,CAAC;IACtB,0CAA0C;IAC1C,kBAAkB,EAAE,OAAO,CAAC;IAC5B,iDAAiD;IACjD,gBAAgB,EAAE,OAAO,CAAC;IAC1B,iCAAiC;IACjC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,8BAA8B;IAC9B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oBAAoB;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAqBD;;;;GAIG;AACH,cAAM,gBAAiB,YAAW,iBAAiB;IACjD,EAAE,SAAc;IAChB,IAAI,SAAyC;IAC7C,OAAO,SAAe;IAEtB,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,OAAO,CAAiC;IAChD,OAAO,CAAC,gBAAgB,CAA6B;gBAEzC,MAAM,GAAE,OAAO,CAAC,aAAa,CAAM;IAI/C;;OAEG;IACG,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBtD;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAQ/B;;OAEG;YACW,cAAc;IA4C5B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAMxB;;OAEG;YACW,eAAe;IA6C7B;;OAEG;IACH,eAAe,IAAI,MAAM;IAIzB;;OAEG;IACH,kBAAkB,IAAI,OAAO;CAG9B;AAED;;GAEG;AACH,MAAM,CAAC,OAAO,UAAU,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,GAAG,iBAAiB,CAEzF;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI,CA4BrD;AAGD,OAAO,EAAE,gBAAgB,EAAE,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -16,12 +16,12 @@ const DEFAULT_CONFIG = {
|
|
|
16
16
|
dashboardHost: "127.0.0.1",
|
|
17
17
|
autoStartDashboard: true,
|
|
18
18
|
autoCheckUpdates: true,
|
|
19
|
-
featureJobs:
|
|
20
|
-
featureSkills:
|
|
21
|
-
featureMemory:
|
|
22
|
-
featurePolicy:
|
|
23
|
-
featureMetering:
|
|
24
|
-
featureApprovals:
|
|
19
|
+
featureJobs: true,
|
|
20
|
+
featureSkills: true,
|
|
21
|
+
featureMemory: true,
|
|
22
|
+
featurePolicy: true,
|
|
23
|
+
featureMetering: true,
|
|
24
|
+
featureApprovals: true,
|
|
25
25
|
};
|
|
26
26
|
const GITHUB_RELEASES_URL = "https://api.github.com/repos/oabdelmaksoud/AGI-FARM-PLUGIN/releases/latest";
|
|
27
27
|
// โโ AGI Farm Extension โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -175,6 +175,34 @@ class AGIFarmExtension {
|
|
|
175
175
|
export default function createExtension(config) {
|
|
176
176
|
return new AGIFarmExtension(config);
|
|
177
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* OpenClaw plugin registration entrypoint for runtimes that expect `register`.
|
|
180
|
+
*/
|
|
181
|
+
export function register(context) {
|
|
182
|
+
const extension = new AGIFarmExtension(context.pluginConfig ?? {});
|
|
183
|
+
const start = async () => {
|
|
184
|
+
await extension.onLoad({
|
|
185
|
+
logger: context.logger,
|
|
186
|
+
config: {},
|
|
187
|
+
});
|
|
188
|
+
context.logger.info(`[agi-farm] Dashboard URL: ${extension.getDashboardUrl()}`);
|
|
189
|
+
};
|
|
190
|
+
const stop = async () => {
|
|
191
|
+
await extension.onUnload();
|
|
192
|
+
};
|
|
193
|
+
if (typeof context.registerService === "function") {
|
|
194
|
+
context.registerService({
|
|
195
|
+
id: "agi-farm-dashboard",
|
|
196
|
+
start,
|
|
197
|
+
stop,
|
|
198
|
+
});
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
start().catch((err) => {
|
|
202
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
203
|
+
context.logger.error(`[agi-farm] Failed to initialize: ${message}`);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
178
206
|
// Export types
|
|
179
207
|
export { AGIFarmExtension };
|
|
180
208
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAEvC,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,aAAa,CAAC,IAAI,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAEtF,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,WAAW,GAAY,OAAO,CAAC,iBAAiB,CAAyB,CAAC,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/openclaw.plugin.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"id": "agi-farm",
|
|
3
3
|
"kind": "team-orchestration",
|
|
4
4
|
"name": "AGI Farm โ Multi-Agent Team Builder",
|
|
5
|
-
"version": "1.
|
|
5
|
+
"version": "1.2.2",
|
|
6
6
|
"description": "Bootstrap complete multi-agent AI teams with auto-dispatcher, live dashboard, and infrastructure. Includes interactive wizard, SOUL.md generation, comms setup, cron registration, and React + SSE ops room.",
|
|
7
7
|
"author": "oabdelmaksoud",
|
|
8
8
|
"homepage": "https://github.com/oabdelmaksoud/AGI-FARM-PLUGIN",
|
|
@@ -42,32 +42,32 @@
|
|
|
42
42
|
},
|
|
43
43
|
"featureJobs": {
|
|
44
44
|
"type": "boolean",
|
|
45
|
-
"default":
|
|
45
|
+
"default": true,
|
|
46
46
|
"description": "Enable jobs runtime APIs and background worker"
|
|
47
47
|
},
|
|
48
48
|
"featureSkills": {
|
|
49
49
|
"type": "boolean",
|
|
50
|
-
"default":
|
|
50
|
+
"default": true,
|
|
51
51
|
"description": "Enable skills registry APIs"
|
|
52
52
|
},
|
|
53
53
|
"featureMemory": {
|
|
54
54
|
"type": "boolean",
|
|
55
|
-
"default":
|
|
55
|
+
"default": true,
|
|
56
56
|
"description": "Enable memory indexing and search APIs"
|
|
57
57
|
},
|
|
58
58
|
"featurePolicy": {
|
|
59
59
|
"type": "boolean",
|
|
60
|
-
"default":
|
|
60
|
+
"default": true,
|
|
61
61
|
"description": "Enable policy engine for runtime actions"
|
|
62
62
|
},
|
|
63
63
|
"featureMetering": {
|
|
64
64
|
"type": "boolean",
|
|
65
|
-
"default":
|
|
65
|
+
"default": true,
|
|
66
66
|
"description": "Enable usage metering APIs and dashboard data"
|
|
67
67
|
},
|
|
68
68
|
"featureApprovals": {
|
|
69
69
|
"type": "boolean",
|
|
70
|
-
"default":
|
|
70
|
+
"default": true,
|
|
71
71
|
"description": "Enable approval workflows for policy-gated actions"
|
|
72
72
|
}
|
|
73
73
|
},
|
|
@@ -163,4 +163,4 @@
|
|
|
163
163
|
"handler": "./scripts/teardown.js"
|
|
164
164
|
}
|
|
165
165
|
]
|
|
166
|
-
}
|
|
166
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agi-farm",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.2",
|
|
4
4
|
"description": "Multi-agent AI team builder for OpenClaw โ bootstrap complete teams with auto-dispatcher, dashboard, and infrastructure",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -52,6 +52,8 @@
|
|
|
52
52
|
"dev": "tsc --watch",
|
|
53
53
|
"typecheck": "tsc --noEmit",
|
|
54
54
|
"test": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js",
|
|
55
|
+
"test:install": "node --experimental-vm-modules ./node_modules/jest/bin/jest.js --runInBand tests/install-*.test.js",
|
|
56
|
+
"test:install:real-openclaw": "bash scripts/test-install-smoke.sh",
|
|
55
57
|
"start-dashboard": "node server/dashboard.js",
|
|
56
58
|
"lint": "eslint src --ext .ts,.js"
|
|
57
59
|
},
|
package/scripts/dashboard.js
CHANGED
|
File without changes
|
package/scripts/dispatch.js
CHANGED
|
@@ -1,60 +1,203 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* AGI Farm Auto-Dispatcher
|
|
4
|
+
*
|
|
5
|
+
* Reads TASKS.json and fires openclaw agent sessions for pending tasks.
|
|
6
|
+
* Pure Node.js โ no Python dependency required.
|
|
7
|
+
*/
|
|
8
|
+
|
|
2
9
|
import fs from 'fs';
|
|
3
10
|
import path from 'path';
|
|
4
11
|
import os from 'os';
|
|
5
12
|
import chalk from 'chalk';
|
|
6
|
-
import {
|
|
13
|
+
import { spawnSync } from 'child_process';
|
|
7
14
|
import { fileURLToPath } from 'url';
|
|
8
15
|
|
|
9
16
|
const __filename = fileURLToPath(import.meta.url);
|
|
10
17
|
const __dirname = path.dirname(__filename);
|
|
11
18
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
+
const WORKSPACE = process.env.AGI_FARM_WORKSPACE || path.join(os.homedir(), '.openclaw', 'workspace');
|
|
20
|
+
const BUNDLE_DIR = path.join(WORKSPACE, 'agi-farm-bundle');
|
|
21
|
+
const TASKS_FILE = path.join(WORKSPACE, 'TASKS.json');
|
|
22
|
+
const LOG_DIR = path.join(WORKSPACE, 'logs');
|
|
23
|
+
const STALE_MINUTES = 30;
|
|
24
|
+
const MAX_CONCURRENT = 3;
|
|
19
25
|
|
|
20
26
|
const args = process.argv.slice(2);
|
|
21
27
|
const execute = args.includes('--execute');
|
|
22
|
-
const
|
|
23
|
-
const
|
|
28
|
+
const dryRun = !execute;
|
|
29
|
+
const verbose = args.includes('--verbose');
|
|
24
30
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
// โโ Helpers โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
32
|
+
function readJson(file, fallback = null) {
|
|
33
|
+
try { return JSON.parse(fs.readFileSync(file, 'utf-8')); }
|
|
34
|
+
catch { return fallback; }
|
|
28
35
|
}
|
|
29
36
|
|
|
30
|
-
|
|
37
|
+
function writeJson(file, data) {
|
|
38
|
+
fs.writeFileSync(file, JSON.stringify(data, null, 2), 'utf-8');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function log(msg) {
|
|
42
|
+
const ts = new Date().toISOString();
|
|
43
|
+
const line = `[${ts}] ${msg}`;
|
|
44
|
+
console.log(line);
|
|
45
|
+
try {
|
|
46
|
+
fs.mkdirSync(LOG_DIR, { recursive: true });
|
|
47
|
+
fs.appendFileSync(path.join(LOG_DIR, 'auto-dispatch.log'), line + '\n');
|
|
48
|
+
} catch { /* log write failure is non-fatal */ }
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function getAgentStatus() {
|
|
52
|
+
return readJson(path.join(WORKSPACE, 'AGENT_STATUS.json'), {});
|
|
53
|
+
}
|
|
31
54
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
55
|
+
function getBusyAgents() {
|
|
56
|
+
const result = spawnSync('openclaw', ['agents', 'list', '--json'], {
|
|
57
|
+
encoding: 'utf-8', timeout: 10000,
|
|
58
|
+
});
|
|
59
|
+
if (result.status !== 0) return new Set();
|
|
60
|
+
try {
|
|
61
|
+
const agents = JSON.parse(result.stdout);
|
|
62
|
+
return new Set(agents.filter(a => a.sessionCount > 0).map(a => a.id));
|
|
63
|
+
} catch { return new Set(); }
|
|
35
64
|
}
|
|
36
65
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
console.log(chalk.dim('Firing agent sessions for pending tasks...\n'));
|
|
66
|
+
function isStale(task) {
|
|
67
|
+
if (!task.updated_at && !task.started_at) return false;
|
|
68
|
+
const ref = task.updated_at || task.started_at;
|
|
69
|
+
const ageMs = Date.now() - new Date(ref).getTime();
|
|
70
|
+
return ageMs > STALE_MINUTES * 60 * 1000;
|
|
43
71
|
}
|
|
44
72
|
|
|
45
|
-
|
|
46
|
-
|
|
73
|
+
function dependenciesMet(task, allTasks) {
|
|
74
|
+
if (!task.depends_on || task.depends_on.length === 0) return true;
|
|
75
|
+
const byId = Object.fromEntries(allTasks.map(t => [t.id, t]));
|
|
76
|
+
return task.depends_on.every(dep => byId[dep]?.status === 'complete');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function dispatchTask(task, agentId) {
|
|
80
|
+
const cmd = [
|
|
81
|
+
'sessions', 'start',
|
|
82
|
+
'--agent', agentId,
|
|
83
|
+
'--message', `[AUTO-DISPATCH] Task assigned: ${task.id}\nTitle: ${task.title || '(untitled)'}\n${task.description || ''}`.trim(),
|
|
84
|
+
'--non-interactive',
|
|
85
|
+
];
|
|
86
|
+
if (verbose) log(` โ openclaw ${cmd.join(' ')}`);
|
|
87
|
+
return spawnSync('openclaw', cmd, { encoding: 'utf-8', timeout: 15000 });
|
|
88
|
+
}
|
|
47
89
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
90
|
+
// โโ Main โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
91
|
+
console.log(chalk.cyan.bold('\nโก AGI Farm โ Auto-Dispatcher\n'));
|
|
92
|
+
|
|
93
|
+
if (dryRun) {
|
|
94
|
+
console.log(chalk.yellow('Dry-run mode (preview only). Use --execute to run.\n'));
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Check for team.json
|
|
98
|
+
const team = readJson(path.join(BUNDLE_DIR, 'team.json'));
|
|
99
|
+
if (!team) {
|
|
100
|
+
console.error(chalk.red('Error: No team bundle found. Run agi-farm setup first.'));
|
|
51
101
|
process.exit(1);
|
|
52
|
-
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Load tasks
|
|
105
|
+
const tasks = readJson(TASKS_FILE, []);
|
|
106
|
+
if (!Array.isArray(tasks)) {
|
|
107
|
+
console.error(chalk.red('Error: TASKS.json is invalid or missing.'));
|
|
108
|
+
process.exit(1);
|
|
109
|
+
}
|
|
53
110
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
111
|
+
const agentStatus = getAgentStatus();
|
|
112
|
+
const busyAgents = execute ? getBusyAgents() : new Set();
|
|
113
|
+
|
|
114
|
+
// Eligible agents (non-main, non-busy, available)
|
|
115
|
+
const eligibleAgents = team.agents
|
|
116
|
+
.filter(a => a.id !== 'main')
|
|
117
|
+
.filter(a => !busyAgents.has(a.id))
|
|
118
|
+
.filter(a => {
|
|
119
|
+
const s = agentStatus[a.id];
|
|
120
|
+
return !s || s.status === 'available' || s.status === undefined;
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Pending tasks with no blocking dependencies
|
|
124
|
+
const pending = tasks
|
|
125
|
+
.filter(t => t.status === 'pending')
|
|
126
|
+
.filter(t => dependenciesMet(t, tasks));
|
|
127
|
+
|
|
128
|
+
// Stale in-progress tasks (reset to pending)
|
|
129
|
+
const staleInProgress = tasks.filter(t => t.status === 'in-progress' && isStale(t));
|
|
130
|
+
|
|
131
|
+
console.log(`๐ Tasks: ${tasks.length} total ยท ${pending.length} pending ยท ${staleInProgress.length} stale`);
|
|
132
|
+
console.log(`๐ค Agents: ${eligibleAgents.length} available\n`);
|
|
133
|
+
|
|
134
|
+
// Reset stale tasks
|
|
135
|
+
if (staleInProgress.length > 0) {
|
|
136
|
+
log(`Resetting ${staleInProgress.length} stale in-progress task(s)...`);
|
|
137
|
+
if (execute) {
|
|
138
|
+
for (const t of staleInProgress) {
|
|
139
|
+
t.status = 'pending';
|
|
140
|
+
t.stale_reset_at = new Date().toISOString();
|
|
141
|
+
}
|
|
142
|
+
writeJson(TASKS_FILE, tasks);
|
|
57
143
|
} else {
|
|
58
|
-
|
|
144
|
+
for (const t of staleInProgress) {
|
|
145
|
+
console.log(chalk.dim(` Would reset stale task: ${t.id} "${t.title || ''}"`));
|
|
146
|
+
}
|
|
59
147
|
}
|
|
60
|
-
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// HITL notification
|
|
151
|
+
const hitl = tasks.filter(t => t.status === 'needs_human_decision');
|
|
152
|
+
if (hitl.length > 0) {
|
|
153
|
+
console.log(chalk.yellow(`โ ${hitl.length} task(s) need human decision:`));
|
|
154
|
+
for (const t of hitl.slice(0, 5)) {
|
|
155
|
+
console.log(chalk.yellow(` โข [${t.id}] ${t.title || '(untitled)'}`));
|
|
156
|
+
}
|
|
157
|
+
console.log();
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (pending.length === 0) {
|
|
161
|
+
console.log(chalk.dim('No pending tasks to dispatch.\n'));
|
|
162
|
+
process.exit(0);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Dispatch up to MAX_CONCURRENT tasks
|
|
166
|
+
let dispatched = 0;
|
|
167
|
+
const agentQueue = [...eligibleAgents];
|
|
168
|
+
|
|
169
|
+
for (const task of pending) {
|
|
170
|
+
if (dispatched >= MAX_CONCURRENT || agentQueue.length === 0) break;
|
|
171
|
+
|
|
172
|
+
const assignedAgent = task.assigned_to
|
|
173
|
+
? agentQueue.find(a => a.id === task.assigned_to) || agentQueue[0]
|
|
174
|
+
: agentQueue[0];
|
|
175
|
+
|
|
176
|
+
if (dryRun) {
|
|
177
|
+
console.log(chalk.blue(` [DRY-RUN] Would dispatch task "${task.id}" โ agent "${assignedAgent.id}"`));
|
|
178
|
+
} else {
|
|
179
|
+
log(`Dispatching task "${task.id}" โ agent "${assignedAgent.id}"`);
|
|
180
|
+
const result = dispatchTask(task, assignedAgent.id);
|
|
181
|
+
if (result.status === 0) {
|
|
182
|
+
// Mark as in-progress
|
|
183
|
+
task.status = 'in-progress';
|
|
184
|
+
task.started_at = new Date().toISOString();
|
|
185
|
+
task.assigned_to = assignedAgent.id;
|
|
186
|
+
log(` โ
Dispatched: ${task.id}`);
|
|
187
|
+
} else {
|
|
188
|
+
log(` โ Dispatch failed for ${task.id}: ${result.stderr?.trim()}`);
|
|
189
|
+
}
|
|
190
|
+
agentQueue.shift(); // each agent takes one task
|
|
191
|
+
}
|
|
192
|
+
dispatched++;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (execute && dispatched > 0) {
|
|
196
|
+
writeJson(TASKS_FILE, tasks);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
console.log(
|
|
200
|
+
execute
|
|
201
|
+
? chalk.green(`\nโ
Dispatched ${dispatched} task(s).\n`)
|
|
202
|
+
: chalk.dim(`\n(Dry-run complete. ${dispatched} task(s) would be dispatched.)\n`)
|
|
203
|
+
);
|
package/scripts/export.js
CHANGED
|
@@ -21,18 +21,40 @@ console.log(chalk.cyan.bold('\n๐ค AGI Farm โ Export to GitHub\n'));
|
|
|
21
21
|
// Check for bundle
|
|
22
22
|
if (!fs.existsSync(BUNDLE_DIR)) {
|
|
23
23
|
console.error(chalk.red('Error: Bundle not found at'), BUNDLE_DIR);
|
|
24
|
-
console.log(chalk.yellow('Run
|
|
24
|
+
console.log(chalk.yellow('Run agi-farm setup first.'));
|
|
25
25
|
process.exit(1);
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
// Check for git
|
|
29
29
|
const gitDir = path.join(BUNDLE_DIR, '.git');
|
|
30
30
|
if (!fs.existsSync(gitDir)) {
|
|
31
|
-
console.log(chalk.yellow('No git repository found in bundle'));
|
|
32
|
-
console.log(chalk.dim('
|
|
31
|
+
console.log(chalk.yellow('No git repository found in the bundle directory.'));
|
|
32
|
+
console.log(chalk.dim('\nTo set one up manually:'));
|
|
33
|
+
console.log(chalk.white(` cd "${BUNDLE_DIR}"`));
|
|
34
|
+
console.log(chalk.white(' git init -b main'));
|
|
35
|
+
console.log(chalk.white(' git add .'));
|
|
36
|
+
console.log(chalk.white(' git commit -m "feat: initial AGI Farm bundle"'));
|
|
37
|
+
console.log(chalk.white(' git remote add origin <your-repo-url>'));
|
|
38
|
+
console.log(chalk.white(' git push -u origin main'));
|
|
39
|
+
console.log(chalk.dim('\nOr re-run agi-farm setup and choose the GitHub option.\n'));
|
|
33
40
|
process.exit(0);
|
|
34
41
|
}
|
|
35
42
|
|
|
43
|
+
// Check for remote
|
|
44
|
+
const remoteResult = spawnSync('git', ['remote', 'get-url', 'origin'], {
|
|
45
|
+
encoding: 'utf-8', cwd: BUNDLE_DIR,
|
|
46
|
+
});
|
|
47
|
+
if (remoteResult.status !== 0) {
|
|
48
|
+
console.log(chalk.yellow('No git remote configured.'));
|
|
49
|
+
console.log(chalk.dim('\nTo add a remote:'));
|
|
50
|
+
console.log(chalk.white(` cd "${BUNDLE_DIR}"`));
|
|
51
|
+
console.log(chalk.white(' git remote add origin <your-repo-url>'));
|
|
52
|
+
console.log(chalk.white(' git push -u origin main\n'));
|
|
53
|
+
process.exit(0);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const remoteUrl = remoteResult.stdout.trim();
|
|
57
|
+
|
|
36
58
|
try {
|
|
37
59
|
// Add all changes
|
|
38
60
|
console.log(chalk.dim('Staging changes...'));
|
|
@@ -44,12 +66,11 @@ try {
|
|
|
44
66
|
const commitResult = spawnSync('git', ['commit', '-m', `export: ${date}`], {
|
|
45
67
|
encoding: 'utf-8',
|
|
46
68
|
stdio: 'inherit',
|
|
47
|
-
cwd: BUNDLE_DIR
|
|
69
|
+
cwd: BUNDLE_DIR,
|
|
48
70
|
});
|
|
49
71
|
|
|
50
72
|
if (commitResult.status !== 0) {
|
|
51
|
-
console.log(chalk.
|
|
52
|
-
process.exit(0);
|
|
73
|
+
console.log(chalk.dim('Nothing new to commit.'));
|
|
53
74
|
}
|
|
54
75
|
|
|
55
76
|
// Push
|
|
@@ -58,24 +79,16 @@ try {
|
|
|
58
79
|
|
|
59
80
|
if (pushResult.status === 0) {
|
|
60
81
|
console.log(chalk.green('\nโ
Bundle exported to GitHub'));
|
|
82
|
+
console.log(chalk.dim(` Remote: ${remoteUrl}\n`));
|
|
61
83
|
} else {
|
|
62
|
-
console.log(chalk.yellow('
|
|
84
|
+
console.log(chalk.yellow('\nPush failed. Check your git credentials or remote URL.'));
|
|
85
|
+
console.log(chalk.dim(` Remote: ${remoteUrl}`));
|
|
86
|
+
console.log(chalk.dim(' Try: git push --set-upstream origin main\n'));
|
|
87
|
+
process.exit(1);
|
|
63
88
|
}
|
|
64
89
|
|
|
65
|
-
// Get remote URL
|
|
66
|
-
try {
|
|
67
|
-
const urlResult = spawnSync('git', ['remote', 'get-url', 'origin'], {
|
|
68
|
-
encoding: 'utf-8',
|
|
69
|
-
cwd: BUNDLE_DIR
|
|
70
|
-
});
|
|
71
|
-
if (urlResult.status === 0) {
|
|
72
|
-
const url = urlResult.stdout.trim();
|
|
73
|
-
console.log(chalk.dim(`\nRemote: ${url}\n`));
|
|
74
|
-
}
|
|
75
|
-
} catch { }
|
|
76
|
-
|
|
77
90
|
} catch (err) {
|
|
78
|
-
console.error(chalk.red('Error exporting to GitHub'));
|
|
79
|
-
console.log(chalk.
|
|
91
|
+
console.error(chalk.red('Error exporting to GitHub:'), err.message);
|
|
92
|
+
console.log(chalk.dim('Make sure you have push access to the remote.'));
|
|
80
93
|
process.exit(1);
|
|
81
94
|
}
|