@rethinkingstudio/clawpilot 1.0.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 +79 -0
- package/dist/commands/install.d.ts +4 -0
- package/dist/commands/install.js +105 -0
- package/dist/commands/install.js.map +1 -0
- package/dist/commands/pair.d.ts +6 -0
- package/dist/commands/pair.js +60 -0
- package/dist/commands/pair.js.map +1 -0
- package/dist/commands/run.d.ts +1 -0
- package/dist/commands/run.js +27 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/status.d.ts +1 -0
- package/dist/commands/status.js +56 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/config/config.d.ts +22 -0
- package/dist/config/config.js +53 -0
- package/dist/config/config.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +63 -0
- package/dist/index.js.map +1 -0
- package/dist/relay/gateway-client.d.ts +34 -0
- package/dist/relay/gateway-client.js +268 -0
- package/dist/relay/gateway-client.js.map +1 -0
- package/dist/relay/reconnect.d.ts +14 -0
- package/dist/relay/reconnect.js +27 -0
- package/dist/relay/reconnect.js.map +1 -0
- package/dist/relay/relay-manager.d.ts +19 -0
- package/dist/relay/relay-manager.js +101 -0
- package/dist/relay/relay-manager.js.map +1 -0
- package/dist/relay/session-proxy.d.ts +18 -0
- package/dist/relay/session-proxy.js +75 -0
- package/dist/relay/session-proxy.js.map +1 -0
- package/package.json +26 -0
- package/run-relay.sh +24 -0
- package/src/commands/install.ts +110 -0
- package/src/commands/pair.ts +84 -0
- package/src/commands/run.ts +33 -0
- package/src/commands/status.ts +58 -0
- package/src/config/config.ts +67 -0
- package/src/index.ts +70 -0
- package/src/relay/gateway-client.ts +333 -0
- package/src/relay/reconnect.ts +37 -0
- package/src/relay/relay-manager.ts +148 -0
- package/test-chat.mjs +64 -0
- package/test-direct.mjs +171 -0
- package/tsconfig.json +16 -0
package/test-direct.mjs
ADDED
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Direct test: connect to OpenClaw Gateway and send chat.send
|
|
3
|
+
* Uses same logic as gateway-client.ts
|
|
4
|
+
*/
|
|
5
|
+
import { WebSocket } from "ws";
|
|
6
|
+
import { randomUUID, generateKeyPairSync, createPrivateKey, sign, createPublicKey, createHash } from "node:crypto";
|
|
7
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
|
|
11
|
+
const IDENTITY_PATH = join(homedir(), ".clawai", "device-identity.json");
|
|
12
|
+
const ED25519_SPKI_PREFIX = Buffer.from("302a300506032b6570032100", "hex");
|
|
13
|
+
|
|
14
|
+
function base64UrlEncode(buf) {
|
|
15
|
+
return buf.toString("base64").replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/g, "");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function rawPublicKeyBytes(publicKeyPem) {
|
|
19
|
+
const key = createPublicKey(publicKeyPem);
|
|
20
|
+
const spki = key.export({ type: "spki", format: "der" });
|
|
21
|
+
if (spki.length === ED25519_SPKI_PREFIX.length + 32 && spki.subarray(0, ED25519_SPKI_PREFIX.length).equals(ED25519_SPKI_PREFIX)) {
|
|
22
|
+
return spki.subarray(ED25519_SPKI_PREFIX.length);
|
|
23
|
+
}
|
|
24
|
+
return spki;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const stored = JSON.parse(readFileSync(IDENTITY_PATH, "utf8"));
|
|
28
|
+
const identity = { deviceId: stored.deviceId, publicKeyPem: stored.publicKeyPem, privateKeyPem: stored.privateKeyPem };
|
|
29
|
+
|
|
30
|
+
const openclawCfg = JSON.parse(readFileSync(join(homedir(), ".openclaw", "openclaw.json"), "utf8"));
|
|
31
|
+
const authToken = openclawCfg?.gateway?.auth?.token;
|
|
32
|
+
const port = openclawCfg?.gateway?.port ?? 18789;
|
|
33
|
+
|
|
34
|
+
console.log("Device ID:", identity.deviceId);
|
|
35
|
+
console.log("Auth token:", authToken ? authToken.slice(0, 8) + "..." : "(none)");
|
|
36
|
+
console.log("Connecting to ws://localhost:" + port);
|
|
37
|
+
|
|
38
|
+
const ws = new WebSocket("ws://localhost:" + port, { maxPayload: 25 * 1024 * 1024 });
|
|
39
|
+
const pending = new Map();
|
|
40
|
+
let connectNonce = null;
|
|
41
|
+
let connectSent = false;
|
|
42
|
+
let connected = false;
|
|
43
|
+
|
|
44
|
+
function sendFrame(obj) {
|
|
45
|
+
ws.send(JSON.stringify(obj));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function request(method, params) {
|
|
49
|
+
const id = randomUUID();
|
|
50
|
+
return new Promise((resolve, reject) => {
|
|
51
|
+
pending.set(id, { resolve, reject });
|
|
52
|
+
sendFrame({ type: "req", id, method, params });
|
|
53
|
+
console.log(`→ req [${id.slice(0, 8)}] ${method}`);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function sendConnect(nonce) {
|
|
58
|
+
if (connectSent) return;
|
|
59
|
+
connectSent = true;
|
|
60
|
+
const role = "operator";
|
|
61
|
+
const scopes = ["operator.admin"];
|
|
62
|
+
const clientId = "gateway-client";
|
|
63
|
+
const clientMode = "backend";
|
|
64
|
+
const signedAtMs = Date.now();
|
|
65
|
+
|
|
66
|
+
const version = nonce ? "v2" : "v1";
|
|
67
|
+
const payload = [version, identity.deviceId, clientId, clientMode, role, scopes.join(","), String(signedAtMs), authToken ?? "", ...(nonce ? [nonce] : [])].join("|");
|
|
68
|
+
const key = createPrivateKey(identity.privateKeyPem);
|
|
69
|
+
const signature = base64UrlEncode(sign(null, Buffer.from(payload, "utf8"), key));
|
|
70
|
+
const device = {
|
|
71
|
+
id: identity.deviceId,
|
|
72
|
+
publicKey: base64UrlEncode(rawPublicKeyBytes(identity.publicKeyPem)),
|
|
73
|
+
signature,
|
|
74
|
+
signedAt: signedAtMs,
|
|
75
|
+
nonce,
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const params = {
|
|
79
|
+
minProtocol: 3, maxProtocol: 3,
|
|
80
|
+
role, scopes, caps: [], commands: [],
|
|
81
|
+
client: { id: clientId, displayName: "ClawAI Direct Test", version: "1.0.0", platform: process.platform, mode: clientMode },
|
|
82
|
+
device,
|
|
83
|
+
auth: authToken ? { token: authToken } : undefined,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
request("connect", params)
|
|
87
|
+
.then((res) => {
|
|
88
|
+
console.log("✓ connect OK:", JSON.stringify(res).slice(0, 100));
|
|
89
|
+
connected = true;
|
|
90
|
+
runTest();
|
|
91
|
+
})
|
|
92
|
+
.catch((err) => {
|
|
93
|
+
console.error("✗ connect FAILED:", err.message);
|
|
94
|
+
process.exit(1);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async function runTest() {
|
|
99
|
+
console.log("\n--- Running test ---");
|
|
100
|
+
try {
|
|
101
|
+
const r1 = await request("sessions.reset", { key: "main" });
|
|
102
|
+
console.log("✓ sessions.reset:", JSON.stringify(r1));
|
|
103
|
+
|
|
104
|
+
const r2 = await request("chat.send", {
|
|
105
|
+
sessionKey: "main",
|
|
106
|
+
message: "hello, please reply with just one short sentence",
|
|
107
|
+
idempotencyKey: randomUUID(),
|
|
108
|
+
});
|
|
109
|
+
console.log("✓ chat.send:", JSON.stringify(r2));
|
|
110
|
+
console.log("\nWaiting for chat events...");
|
|
111
|
+
} catch (err) {
|
|
112
|
+
console.error("✗ command failed:", err.message);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
ws.on("open", () => {
|
|
118
|
+
console.log("WebSocket open, waiting for challenge...");
|
|
119
|
+
setTimeout(() => { if (!connectSent) { console.log("(no challenge, sending connect now)"); sendConnect(null); } }, 1000);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
ws.on("message", (raw) => {
|
|
123
|
+
const msg = JSON.parse(raw.toString());
|
|
124
|
+
if (msg.type === "event") {
|
|
125
|
+
if (msg.event === "connect.challenge") {
|
|
126
|
+
const nonce = msg.payload?.nonce;
|
|
127
|
+
console.log("← challenge nonce:", nonce);
|
|
128
|
+
sendConnect(nonce ?? null);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (msg.event === "chat") {
|
|
132
|
+
const p = msg.payload;
|
|
133
|
+
if (p.state === "delta") {
|
|
134
|
+
process.stdout.write("\r[delta] " + (p.message?.content?.[0]?.text ?? "").slice(-80).padEnd(80));
|
|
135
|
+
} else if (p.state === "final") {
|
|
136
|
+
console.log("\n\n[FINAL] " + (p.message?.content?.[0]?.text ?? "(no text)"));
|
|
137
|
+
ws.close();
|
|
138
|
+
process.exit(0);
|
|
139
|
+
} else if (p.state === "error") {
|
|
140
|
+
console.log("\n[ERROR]", p.errorMessage);
|
|
141
|
+
ws.close();
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
} else if (msg.event !== "health" && msg.event !== "presence" && msg.event !== "tick") {
|
|
145
|
+
console.log("\n← event:", msg.event, JSON.stringify(msg.payload ?? {}).slice(0, 80));
|
|
146
|
+
}
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (msg.type === "res") {
|
|
150
|
+
const p = pending.get(msg.id);
|
|
151
|
+
if (p) {
|
|
152
|
+
pending.delete(msg.id);
|
|
153
|
+
console.log(`← res [${msg.id.slice(0, 8)}] ok=${msg.ok}` + (msg.error ? " error=" + msg.error.message : ""));
|
|
154
|
+
if (msg.ok) p.resolve(msg.payload);
|
|
155
|
+
else p.reject(new Error(msg.error?.message ?? "gateway error"));
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
ws.on("close", (code, reason) => {
|
|
161
|
+
console.log("\n← close", code, reason.toString() || "(no reason)");
|
|
162
|
+
process.exit(code === 1000 ? 0 : 1);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
ws.on("error", (err) => console.error("WS error:", err.message));
|
|
166
|
+
|
|
167
|
+
setTimeout(() => {
|
|
168
|
+
console.log("\n[timeout 60s]");
|
|
169
|
+
ws.close();
|
|
170
|
+
process.exit(1);
|
|
171
|
+
}, 60000);
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"declaration": true,
|
|
12
|
+
"sourceMap": true
|
|
13
|
+
},
|
|
14
|
+
"include": ["src/**/*"],
|
|
15
|
+
"exclude": ["node_modules", "dist"]
|
|
16
|
+
}
|