Skip to content

Commit 5161c74

Browse files
committed
draft plugin system
1 parent 4816d71 commit 5161c74

41 files changed

Lines changed: 2938 additions & 507 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

AGENTS.md

Lines changed: 0 additions & 5 deletions
This file was deleted.

CLAWSET.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
# Clawset Integration Guide
2+
3+
You are running inside a **clawset-managed instance**. Clawset is an orchestrator that manages your lifecycle, provides credentials, and connects you with peer agents.
4+
5+
## Directory: `$HOME/clawset/.clawset/`
6+
7+
This is the shared communication directory between you and the clawset host.
8+
9+
### Files You Can READ
10+
11+
| File | Purpose |
12+
|------|---------|
13+
| `context.json` | Info about this instance, peer instances, and available auth |
14+
| `auth/{provider}.json` | Credentials for AI providers (e.g., `auth/openai-codex.json`) |
15+
| `CLAWSET.md` | This file |
16+
17+
### Files You Can WRITE
18+
19+
| File | Purpose |
20+
|------|---------|
21+
| `views.json` | Register web UIs that clawset should display as tabs |
22+
23+
---
24+
25+
## Registering Views
26+
27+
To make a web UI visible in the clawset desktop app, write to `views.json`:
28+
29+
```json
30+
{
31+
"views": [
32+
{
33+
"id": "my-dashboard",
34+
"name": "My Dashboard",
35+
"port": 8080,
36+
"path": "/"
37+
}
38+
]
39+
}
40+
```
41+
42+
Each entry becomes a tab in clawset. You can add/remove entries at any time — clawset polls this file periodically.
43+
44+
**Fields:**
45+
- `id` — unique identifier (lowercase, kebab-case)
46+
- `name` — display name shown in the tab
47+
- `port` — port your web service listens on (bound to `0.0.0.0`)
48+
- `path` — URL path (default: `/`)
49+
50+
---
51+
52+
## Reading Context
53+
54+
`context.json` is written by clawset and tells you about your environment:
55+
56+
```json
57+
{
58+
"orchestrator": "clawset",
59+
"instance": {
60+
"id": "clawset-1",
61+
"ip": "192.168.64.2",
62+
"provider": "multipass"
63+
},
64+
"peers": [
65+
{
66+
"id": "clawset-2",
67+
"ip": "192.168.64.3",
68+
"apps": ["zeroclaw"],
69+
"provider": "multipass"
70+
}
71+
],
72+
"auth": {
73+
"available": ["openai-codex"],
74+
"missing": ["anthropic"]
75+
}
76+
}
77+
```
78+
79+
Use `peers` to discover and communicate with other agent instances.
80+
81+
---
82+
83+
## Using Auth Credentials
84+
85+
Credentials are stored in `auth/{provider-id}.json`. Format depends on the provider type:
86+
87+
**OAuth2 (e.g., openai-codex):**
88+
```json
89+
{
90+
"type": "oauth2",
91+
"access": "eyJ...",
92+
"refresh": "eyJ...",
93+
"expires": 1709235600,
94+
"accountId": "user_..."
95+
}
96+
```
97+
98+
**API Key (e.g., anthropic):**
99+
```json
100+
{
101+
"type": "api_key",
102+
"key": "sk-ant-..."
103+
}
104+
```
105+
106+
Map these to your own config format as needed.
107+
108+
---
109+
110+
## Important Notes
111+
112+
- **Do not delete** `context.json` or `auth/` files — clawset manages these.
113+
- **Do** update `views.json` whenever you start or stop a web service.
114+
- Bind web services to `0.0.0.0`, not `localhost`, so they're accessible from the host.
115+
- This directory is on a shared mount — changes are visible immediately.
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
type: instance-provider
2+
id: multipass
3+
name: Multipass
4+
version: "1.0.0"
5+
description: "Ubuntu VMs via Canonical Multipass"
6+
homepage: https://github.com/webdeb/clawset-multipass
7+
8+
requirements:
9+
binaries: [multipass]
10+
11+
capabilities:
12+
mount: true
13+
transfer: true
14+
shell: true
15+
16+
scripts:
17+
is_available: scripts/is_available.js
18+
create: scripts/create.js
19+
destroy: scripts/destroy.js
20+
start: scripts/start.js
21+
stop: scripts/stop.js
22+
list: scripts/list.js
23+
get: scripts/get.js
24+
exec: scripts/exec.js
25+
transfer: scripts/transfer.js
26+
mount: scripts/mount.js
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Create a new Multipass instance
2+
var name = clawset.env("NAME");
3+
var cpus = clawset.env("CPUS") || "2";
4+
var memory = clawset.env("MEMORY") || "4G";
5+
var disk = clawset.env("DISK") || "20G";
6+
var image = clawset.env("IMAGE") || "lts";
7+
8+
clawset.log("Launching Ubuntu " + image + " instance '" + name + "' (cpus=" + cpus + ", memory=" + memory + ", disk=" + disk + ")...");
9+
10+
var result = clawset.shell("multipass", [
11+
"launch", "-v", image,
12+
"--name", name,
13+
"--cpus", cpus,
14+
"--memory", memory,
15+
"--disk", disk
16+
]);
17+
18+
if (result.exitCode !== 0) {
19+
throw new Error("Failed to launch instance: " + result.stderr);
20+
}
21+
22+
clawset.log("Instance '" + name + "' created successfully!");
23+
24+
return {
25+
id: name,
26+
name: name,
27+
status: "Running",
28+
provider: "multipass"
29+
};
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Destroy a Multipass instance
2+
var instanceId = clawset.env("INSTANCE_ID");
3+
var result = clawset.shell("multipass", ["delete", instanceId, "--purge"]);
4+
if (result.exitCode !== 0) {
5+
throw new Error("Failed to destroy instance: " + result.stderr);
6+
}
7+
return { success: true };
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Execute a command inside a Multipass instance
2+
var instanceId = clawset.env("INSTANCE_ID");
3+
var cmd = clawset.env("CMD");
4+
5+
var result = clawset.shell("multipass", [
6+
"exec", instanceId, "--", "bash", "-ic", cmd
7+
]);
8+
9+
return {
10+
stdout: result.stdout,
11+
stderr: result.stderr,
12+
exitCode: result.exitCode
13+
};
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Get detailed info for a single Multipass instance
2+
var instanceId = clawset.env("INSTANCE_ID");
3+
var result = clawset.shell("multipass", ["info", instanceId, "--format", "json"]);
4+
5+
if (result.exitCode !== 0) {
6+
clawset.log("multipass info failed: " + result.stderr);
7+
return null;
8+
}
9+
10+
var data = JSON.parse(result.stdout);
11+
var info = data.info && data.info[instanceId];
12+
13+
if (!info) {
14+
return null;
15+
}
16+
17+
// Parse resources
18+
var memory = null;
19+
if (info.memory) {
20+
var memTotal = info.memory.total || 0;
21+
var memUsed = info.memory.used || 0;
22+
memory = Math.round(memUsed / 1024 / 1024) + " MB / " + Math.round(memTotal / 1024 / 1024) + " MB";
23+
}
24+
25+
var cpus = null;
26+
if (info.cpu_count) {
27+
cpus = String(info.cpu_count);
28+
}
29+
30+
var storage = null;
31+
if (info.disks && info.disks.sda1) {
32+
var diskTotal = parseInt(info.disks.sda1.total || "0");
33+
var diskUsed = parseInt(info.disks.sda1.used || "0");
34+
storage = Math.round(diskUsed / 1024 / 1024 / 1024) + " GB / " + Math.round(diskTotal / 1024 / 1024 / 1024) + " GB";
35+
}
36+
37+
// Parse mounts
38+
var mounts = {};
39+
if (info.mounts) {
40+
for (var mountPoint in info.mounts) {
41+
mounts[mountPoint] = info.mounts[mountPoint].source_path || "";
42+
}
43+
}
44+
45+
var ip = "";
46+
if (info.ipv4 && info.ipv4.length > 0) {
47+
ip = info.ipv4[0];
48+
}
49+
50+
// Check node/openclaw/provisioning status inside the VM
51+
var innerResult = clawset.shell("multipass", [
52+
"exec", instanceId, "--", "bash", "-ic",
53+
"source ~/.bashrc; echo '===NODE==='; node -v || echo 'NOT_FOUND'; echo '===OPENCLAW_VER==='; openclaw --version || echo 'NOT_FOUND'; echo '===PROVISIONING==='; if [ -f /tmp/provisioning ]; then echo 'YES'; else echo 'NO'; fi"
54+
]);
55+
56+
var nodeInstalled = null;
57+
var openclawInstalled = false;
58+
var isProvisioning = false;
59+
60+
if (innerResult.exitCode === 0) {
61+
var lines = innerResult.stdout.split("\n");
62+
var section = "";
63+
for (var i = 0; i < lines.length; i++) {
64+
var line = lines[i].trim();
65+
if (line === "===NODE===") { section = "node"; continue; }
66+
if (line === "===OPENCLAW_VER===") { section = "openclaw"; continue; }
67+
if (line === "===PROVISIONING===") { section = "provisioning"; continue; }
68+
69+
if (section === "node" && line !== "NOT_FOUND" && line !== "") {
70+
nodeInstalled = line;
71+
}
72+
if (section === "openclaw" && line !== "NOT_FOUND" && line !== "") {
73+
openclawInstalled = true;
74+
}
75+
if (section === "provisioning") {
76+
isProvisioning = (line === "YES");
77+
}
78+
}
79+
}
80+
81+
return {
82+
id: instanceId,
83+
name: instanceId,
84+
status: info.state || "Unknown",
85+
ip: ip,
86+
provider: "multipass",
87+
meta: {
88+
os: info.image_release || "Ubuntu"
89+
},
90+
resources: {
91+
cpus: cpus,
92+
memory: memory,
93+
storage: storage
94+
},
95+
mounts: mounts,
96+
node_installed: nodeInstalled,
97+
openclaw_installed: openclawInstalled,
98+
is_provisioning: isProvisioning
99+
};
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
// Check if Multipass is available on this host
2+
var result = clawset.shell("multipass", ["version"]);
3+
return result.exitCode === 0;

0 commit comments

Comments
 (0)