@mclawnet/agent 0.1.0 → 0.2.1
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/cli.js +63 -71
- package/dist/backend-adapter.d.ts +53 -0
- package/dist/backend-adapter.d.ts.map +1 -0
- package/dist/chunk-YBQQZNRQ.js +780 -0
- package/dist/chunk-YBQQZNRQ.js.map +1 -0
- package/dist/config.d.ts +12 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/fs-handler.d.ts +39 -0
- package/dist/fs-handler.d.ts.map +1 -0
- package/dist/hub-connection.d.ts +60 -0
- package/dist/hub-connection.d.ts.map +1 -0
- package/dist/index.d.ts +7 -2
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -591
- package/dist/index.js.map +1 -0
- package/dist/session-manager.d.ts +33 -0
- package/dist/session-manager.d.ts.map +1 -0
- package/dist/start.d.ts +15 -82
- package/dist/start.d.ts.map +1 -0
- package/dist/start.js +4 -578
- package/dist/start.js.map +1 -0
- package/package.json +23 -24
package/dist/index.js
CHANGED
|
@@ -1,598 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
heartbeatTimer = null;
|
|
9
|
-
reconnectTimer = null;
|
|
10
|
-
reconnectDelay;
|
|
11
|
-
destroyed = false;
|
|
12
|
-
hubUrl;
|
|
13
|
-
token;
|
|
14
|
-
heartbeatInterval;
|
|
15
|
-
maxReconnectDelay;
|
|
16
|
-
onMessage;
|
|
17
|
-
onConnect;
|
|
18
|
-
onDisconnect;
|
|
19
|
-
onError;
|
|
20
|
-
constructor(opts) {
|
|
21
|
-
this.hubUrl = opts.hubUrl;
|
|
22
|
-
this.token = opts.token;
|
|
23
|
-
this.heartbeatInterval = opts.heartbeatInterval ?? DEFAULT_HEARTBEAT_MS;
|
|
24
|
-
this.reconnectDelay = opts.reconnectDelay ?? DEFAULT_RECONNECT_MS;
|
|
25
|
-
this.maxReconnectDelay = opts.maxReconnectDelay ?? DEFAULT_MAX_RECONNECT_MS;
|
|
26
|
-
this.onMessage = opts.onMessage;
|
|
27
|
-
this.onConnect = opts.onConnect;
|
|
28
|
-
this.onDisconnect = opts.onDisconnect;
|
|
29
|
-
this.onError = opts.onError;
|
|
30
|
-
}
|
|
31
|
-
/** Current WebSocket readyState (or CLOSED if no socket) */
|
|
32
|
-
get readyState() {
|
|
33
|
-
return this.ws?.readyState ?? WebSocket.CLOSED;
|
|
34
|
-
}
|
|
35
|
-
get isConnected() {
|
|
36
|
-
return this.ws?.readyState === WebSocket.OPEN;
|
|
37
|
-
}
|
|
38
|
-
/** Open the connection to the hub */
|
|
39
|
-
connect() {
|
|
40
|
-
if (this.destroyed) return;
|
|
41
|
-
this.cleanup();
|
|
42
|
-
const url = new URL(this.hubUrl);
|
|
43
|
-
url.searchParams.set("token", this.token);
|
|
44
|
-
this.ws = new WebSocket(url.toString());
|
|
45
|
-
this.ws.on("open", () => {
|
|
46
|
-
this.reconnectDelay = DEFAULT_RECONNECT_MS;
|
|
47
|
-
this.startHeartbeat();
|
|
48
|
-
this.onConnect?.();
|
|
49
|
-
});
|
|
50
|
-
this.ws.on("message", (raw) => {
|
|
51
|
-
try {
|
|
52
|
-
const data = JSON.parse(raw.toString());
|
|
53
|
-
this.onMessage?.(data);
|
|
54
|
-
} catch {
|
|
55
|
-
this.onMessage?.(raw.toString());
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
this.ws.on("close", (code, reason) => {
|
|
59
|
-
this.stopHeartbeat();
|
|
60
|
-
this.onDisconnect?.(code, reason.toString());
|
|
61
|
-
this.scheduleReconnect();
|
|
62
|
-
});
|
|
63
|
-
this.ws.on("error", (err) => {
|
|
64
|
-
this.onError?.(err);
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
/** Send a JSON message to the hub */
|
|
68
|
-
send(data) {
|
|
69
|
-
if (!this.ws || this.ws.readyState !== WebSocket.OPEN) return false;
|
|
70
|
-
this.ws.send(JSON.stringify(data));
|
|
71
|
-
return true;
|
|
72
|
-
}
|
|
73
|
-
/** Permanently close and stop reconnecting */
|
|
74
|
-
destroy() {
|
|
75
|
-
this.destroyed = true;
|
|
76
|
-
this.cleanup();
|
|
77
|
-
}
|
|
78
|
-
startHeartbeat() {
|
|
79
|
-
this.stopHeartbeat();
|
|
80
|
-
this.heartbeatTimer = setInterval(() => {
|
|
81
|
-
this.send({ type: "heartbeat", ts: Date.now() });
|
|
82
|
-
}, this.heartbeatInterval);
|
|
83
|
-
}
|
|
84
|
-
stopHeartbeat() {
|
|
85
|
-
if (this.heartbeatTimer) {
|
|
86
|
-
clearInterval(this.heartbeatTimer);
|
|
87
|
-
this.heartbeatTimer = null;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
scheduleReconnect() {
|
|
91
|
-
if (this.destroyed) return;
|
|
92
|
-
this.reconnectTimer = setTimeout(() => {
|
|
93
|
-
this.connect();
|
|
94
|
-
}, this.reconnectDelay);
|
|
95
|
-
this.reconnectDelay = Math.min(this.reconnectDelay * 2, this.maxReconnectDelay);
|
|
96
|
-
}
|
|
97
|
-
cleanup() {
|
|
98
|
-
this.stopHeartbeat();
|
|
99
|
-
if (this.reconnectTimer) {
|
|
100
|
-
clearTimeout(this.reconnectTimer);
|
|
101
|
-
this.reconnectTimer = null;
|
|
102
|
-
}
|
|
103
|
-
if (this.ws) {
|
|
104
|
-
this.ws.removeAllListeners();
|
|
105
|
-
if (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING) {
|
|
106
|
-
this.ws.close();
|
|
107
|
-
}
|
|
108
|
-
this.ws = null;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
// src/config.ts
|
|
114
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
115
|
-
import { join } from "path";
|
|
116
|
-
import { homedir } from "os";
|
|
117
|
-
var CONFIG_DIR = join(homedir(), ".clawnet");
|
|
118
|
-
var CONFIG_FILE = join(CONFIG_DIR, "config.json");
|
|
119
|
-
var DEFAULT_HUB_URL = "wss://clawnet-hub.politeriver-28dd7708.eastasia.azurecontainerapps.io/ws/agent";
|
|
120
|
-
var DEFAULTS = {
|
|
121
|
-
hubUrl: DEFAULT_HUB_URL,
|
|
122
|
-
token: "",
|
|
123
|
-
port: 18789,
|
|
124
|
-
name: `agent-${process.pid}`
|
|
125
|
-
};
|
|
126
|
-
function loadConfig(cliOpts = {}) {
|
|
127
|
-
let fileConfig = {};
|
|
128
|
-
if (existsSync(CONFIG_FILE)) {
|
|
129
|
-
try {
|
|
130
|
-
fileConfig = JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
|
|
131
|
-
} catch {
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
return {
|
|
135
|
-
hubUrl: cliOpts.hubUrl ?? process.env.CLAWNET_HUB_URL ?? fileConfig.hubUrl ?? DEFAULTS.hubUrl,
|
|
136
|
-
token: cliOpts.token ?? process.env.CLAWNET_TOKEN ?? fileConfig.token ?? DEFAULTS.token,
|
|
137
|
-
port: Number(
|
|
138
|
-
cliOpts.port ?? process.env.CLAWNET_PORT ?? fileConfig.port ?? DEFAULTS.port
|
|
139
|
-
),
|
|
140
|
-
name: cliOpts.name ?? process.env.CLAWNET_NAME ?? fileConfig.name ?? DEFAULTS.name
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
function saveConfig(config) {
|
|
144
|
-
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
145
|
-
let existing = {};
|
|
146
|
-
if (existsSync(CONFIG_FILE)) {
|
|
147
|
-
try {
|
|
148
|
-
existing = JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
|
|
149
|
-
} catch {
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
const merged = { ...existing, ...config };
|
|
153
|
-
writeFileSync(CONFIG_FILE, JSON.stringify(merged, null, 2) + "\n");
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// src/gateway-launcher.ts
|
|
157
|
-
import { spawn, execSync } from "child_process";
|
|
158
|
-
import { dirname, join as join2 } from "path";
|
|
159
|
-
import { existsSync as existsSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
160
|
-
import { randomBytes } from "crypto";
|
|
161
|
-
import { fileURLToPath } from "url";
|
|
162
|
-
import { homedir as homedir2 } from "os";
|
|
163
|
-
import { createInterface } from "readline";
|
|
164
|
-
function launchGateway(port) {
|
|
165
|
-
return new Promise((resolvePromise, reject) => {
|
|
166
|
-
const openclawBin = findOpenClawBin();
|
|
167
|
-
console.log(`[openclaw] Using binary: ${openclawBin}`);
|
|
168
|
-
const child = spawn(openclawBin, buildGatewayArgs(port), {
|
|
169
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
170
|
-
env: {
|
|
171
|
-
...process.env,
|
|
172
|
-
OPENCLAW_GATEWAY_PORT: String(port)
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
let started = false;
|
|
176
|
-
const timeout = setTimeout(() => {
|
|
177
|
-
if (!started) {
|
|
178
|
-
started = true;
|
|
179
|
-
resolvePromise(handle);
|
|
180
|
-
}
|
|
181
|
-
}, 15e3);
|
|
182
|
-
const handle = {
|
|
183
|
-
process: child,
|
|
184
|
-
port,
|
|
185
|
-
async close() {
|
|
186
|
-
return new Promise((res) => {
|
|
187
|
-
if (child.killed || child.exitCode !== null) {
|
|
188
|
-
res();
|
|
189
|
-
return;
|
|
190
|
-
}
|
|
191
|
-
child.on("exit", () => res());
|
|
192
|
-
child.kill("SIGTERM");
|
|
193
|
-
setTimeout(() => {
|
|
194
|
-
if (!child.killed) child.kill("SIGKILL");
|
|
195
|
-
res();
|
|
196
|
-
}, 5e3);
|
|
197
|
-
});
|
|
198
|
-
}
|
|
199
|
-
};
|
|
200
|
-
child.stdout?.on("data", (data) => {
|
|
201
|
-
const line = data.toString();
|
|
202
|
-
process.stdout.write(`[openclaw] ${line}`);
|
|
203
|
-
if (!started && (line.includes("ready") || line.includes("listening") || line.includes("Gateway"))) {
|
|
204
|
-
started = true;
|
|
205
|
-
clearTimeout(timeout);
|
|
206
|
-
resolvePromise(handle);
|
|
207
|
-
}
|
|
208
|
-
});
|
|
209
|
-
child.stderr?.on("data", (data) => {
|
|
210
|
-
process.stderr.write(`[openclaw] ${data.toString()}`);
|
|
211
|
-
});
|
|
212
|
-
child.on("error", (err) => {
|
|
213
|
-
clearTimeout(timeout);
|
|
214
|
-
if (!started) {
|
|
215
|
-
reject(new Error(`Failed to start OpenClaw: ${err.message}`));
|
|
216
|
-
}
|
|
217
|
-
});
|
|
218
|
-
child.on("exit", (code) => {
|
|
219
|
-
clearTimeout(timeout);
|
|
220
|
-
if (!started) {
|
|
221
|
-
reject(new Error(`OpenClaw exited with code ${code} before starting`));
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
});
|
|
225
|
-
}
|
|
226
|
-
function findOpenClawBin() {
|
|
227
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
228
|
-
const localBin = join2(__dirname, "..", "node_modules", ".bin", "openclaw");
|
|
229
|
-
if (existsSync2(localBin)) return localBin;
|
|
230
|
-
const hoistedBin = join2(__dirname, "..", "..", "..", "node_modules", ".bin", "openclaw");
|
|
231
|
-
if (existsSync2(hoistedBin)) return hoistedBin;
|
|
232
|
-
try {
|
|
233
|
-
const cmd = process.platform === "win32" ? "where openclaw" : "which openclaw";
|
|
234
|
-
const result = execSync(cmd, { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
235
|
-
if (result) return result;
|
|
236
|
-
} catch {
|
|
237
|
-
}
|
|
238
|
-
return "openclaw";
|
|
239
|
-
}
|
|
240
|
-
function buildGatewayArgs(port) {
|
|
241
|
-
return ["gateway", "run", "--port", String(port)];
|
|
242
|
-
}
|
|
243
|
-
var OPENCLAW_DIR_NAME = ".openclaw";
|
|
244
|
-
var OPENCLAW_CONFIG_NAME = "openclaw.json";
|
|
245
|
-
function openclawConfigPath() {
|
|
246
|
-
const dir = join2(homedir2(), OPENCLAW_DIR_NAME);
|
|
247
|
-
return { dir, file: join2(dir, OPENCLAW_CONFIG_NAME) };
|
|
248
|
-
}
|
|
249
|
-
function ensureGatewayToken() {
|
|
250
|
-
const { dir, file } = openclawConfigPath();
|
|
251
|
-
mkdirSync2(dir, { recursive: true });
|
|
252
|
-
let config = {};
|
|
253
|
-
if (existsSync2(file)) {
|
|
254
|
-
try {
|
|
255
|
-
config = JSON.parse(readFileSync2(file, "utf-8"));
|
|
256
|
-
} catch {
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
let dirty = false;
|
|
260
|
-
if (!config.gateway) config.gateway = {};
|
|
261
|
-
if (!config.gateway.auth) config.gateway.auth = {};
|
|
262
|
-
const existing = config.gateway.auth.token;
|
|
263
|
-
let token;
|
|
264
|
-
if (typeof existing === "string" && existing.length > 0) {
|
|
265
|
-
token = existing;
|
|
266
|
-
} else {
|
|
267
|
-
token = randomBytes(16).toString("hex");
|
|
268
|
-
config.gateway.auth.token = token;
|
|
269
|
-
dirty = true;
|
|
270
|
-
}
|
|
271
|
-
if (!config.gateway.mode) {
|
|
272
|
-
config.gateway.mode = "local";
|
|
273
|
-
dirty = true;
|
|
274
|
-
}
|
|
275
|
-
if (dirty) {
|
|
276
|
-
writeFileSync2(file, JSON.stringify(config, null, 2) + "\n");
|
|
277
|
-
}
|
|
278
|
-
return token;
|
|
279
|
-
}
|
|
280
|
-
var COPILOT_TOKEN_URL = "https://api.github.com/copilot_internal/v2/token";
|
|
281
|
-
var COPILOT_HEADERS = {
|
|
282
|
-
"content-type": "application/json",
|
|
283
|
-
"copilot-integration-id": "vscode-chat",
|
|
284
|
-
"editor-version": "vscode/1.104.3",
|
|
285
|
-
"editor-plugin-version": "copilot-chat/0.26.7",
|
|
286
|
-
"user-agent": "GitHubCopilotChat/0.26.7",
|
|
287
|
-
"openai-intent": "conversation-panel",
|
|
288
|
-
"x-github-api-version": "2025-04-01"
|
|
289
|
-
};
|
|
290
|
-
async function getCopilotApiToken(githubToken) {
|
|
291
|
-
const res = await fetch(COPILOT_TOKEN_URL, {
|
|
292
|
-
headers: {
|
|
293
|
-
...COPILOT_HEADERS,
|
|
294
|
-
Authorization: `token ${githubToken}`,
|
|
295
|
-
accept: "application/json"
|
|
296
|
-
}
|
|
297
|
-
});
|
|
298
|
-
if (!res.ok) throw new Error(`Copilot token exchange failed: ${res.status}`);
|
|
299
|
-
const data = await res.json();
|
|
300
|
-
const endpoints = data.endpoints;
|
|
301
|
-
let baseUrl = "https://api.githubcopilot.com";
|
|
302
|
-
if (endpoints && typeof endpoints === "object" && endpoints.api) {
|
|
303
|
-
baseUrl = endpoints.api;
|
|
304
|
-
} else {
|
|
305
|
-
const proxyEp = (data.token || "").match(/(?:^|;)\s*proxy-ep=([^;\s]+)/i)?.[1]?.trim();
|
|
306
|
-
if (proxyEp) {
|
|
307
|
-
baseUrl = proxyEp.startsWith("http") ? proxyEp : `https://${proxyEp}`;
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
return { token: data.token, baseUrl };
|
|
311
|
-
}
|
|
312
|
-
async function fetchCopilotModels(githubToken) {
|
|
313
|
-
try {
|
|
314
|
-
const { token, baseUrl } = await getCopilotApiToken(githubToken);
|
|
315
|
-
const res = await fetch(`${baseUrl}/models`, {
|
|
316
|
-
headers: {
|
|
317
|
-
...COPILOT_HEADERS,
|
|
318
|
-
Authorization: `Bearer ${token}`
|
|
319
|
-
}
|
|
320
|
-
});
|
|
321
|
-
if (!res.ok) throw new Error(`Models fetch failed: ${res.status}`);
|
|
322
|
-
const data = await res.json();
|
|
323
|
-
const models = (data.data || []).filter((m) => m.id && m.capabilities?.type === "chat").map((m) => ({
|
|
324
|
-
model: `github-copilot/${m.id}`,
|
|
325
|
-
display: `${m.name || m.id} (${m.vendor || "unknown"})`
|
|
326
|
-
}));
|
|
327
|
-
return models.length > 0 ? models : fallbackCopilotModels();
|
|
328
|
-
} catch (err) {
|
|
329
|
-
console.warn(` Could not fetch models from Copilot API: ${err.message}`);
|
|
330
|
-
console.warn(" Using default model list.");
|
|
331
|
-
return fallbackCopilotModels();
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
function fallbackCopilotModels() {
|
|
335
|
-
return [
|
|
336
|
-
{ model: "github-copilot/gpt-4o", display: "gpt-4o" },
|
|
337
|
-
{ model: "github-copilot/gpt-4.1", display: "gpt-4.1" },
|
|
338
|
-
{ model: "github-copilot/claude-sonnet-4", display: "Claude Sonnet 4" },
|
|
339
|
-
{ model: "github-copilot/o3-mini", display: "o3-mini" }
|
|
340
|
-
];
|
|
341
|
-
}
|
|
342
|
-
var GITHUB_CLIENT_ID = "Iv1.b507a08c87ecfe98";
|
|
343
|
-
var GITHUB_BASE_URL = "https://github.com";
|
|
344
|
-
async function githubDeviceAuth() {
|
|
345
|
-
console.log("");
|
|
346
|
-
console.log("No GitHub Copilot auth found. Starting GitHub login...");
|
|
347
|
-
console.log("");
|
|
348
|
-
const codeRes = await fetch(`${GITHUB_BASE_URL}/login/device/code`, {
|
|
349
|
-
method: "POST",
|
|
350
|
-
headers: { "content-type": "application/json", accept: "application/json" },
|
|
351
|
-
body: JSON.stringify({ client_id: GITHUB_CLIENT_ID, scope: "read:user" })
|
|
352
|
-
});
|
|
353
|
-
if (!codeRes.ok) throw new Error(`Device code request failed: ${codeRes.status}`);
|
|
354
|
-
const codeData = await codeRes.json();
|
|
355
|
-
console.log(` Open: ${codeData.verification_uri}`);
|
|
356
|
-
console.log(` Code: ${codeData.user_code}`);
|
|
357
|
-
console.log("");
|
|
358
|
-
console.log(" Waiting for authorization...");
|
|
359
|
-
const interval = (codeData.interval + 1) * 1e3;
|
|
360
|
-
const deadline = Date.now() + codeData.expires_in * 1e3;
|
|
361
|
-
while (Date.now() < deadline) {
|
|
362
|
-
await new Promise((r) => setTimeout(r, interval));
|
|
363
|
-
const tokenRes = await fetch(`${GITHUB_BASE_URL}/login/oauth/access_token`, {
|
|
364
|
-
method: "POST",
|
|
365
|
-
headers: { "content-type": "application/json", accept: "application/json" },
|
|
366
|
-
body: JSON.stringify({
|
|
367
|
-
client_id: GITHUB_CLIENT_ID,
|
|
368
|
-
device_code: codeData.device_code,
|
|
369
|
-
grant_type: "urn:ietf:params:oauth:grant-type:device_code"
|
|
370
|
-
})
|
|
371
|
-
});
|
|
372
|
-
if (!tokenRes.ok) continue;
|
|
373
|
-
const tokenData = await tokenRes.json();
|
|
374
|
-
if (tokenData.access_token) {
|
|
375
|
-
console.log(" GitHub login successful!");
|
|
376
|
-
return tokenData.access_token;
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
throw new Error("GitHub device flow timed out");
|
|
380
|
-
}
|
|
381
|
-
function saveGitHubToken(token) {
|
|
382
|
-
const agentDir = join2(homedir2(), ".openclaw", "agents", "main", "agent");
|
|
383
|
-
mkdirSync2(agentDir, { recursive: true });
|
|
384
|
-
const authFile = join2(agentDir, "auth-profiles.json");
|
|
385
|
-
let authData = { version: 1, profiles: {} };
|
|
386
|
-
if (existsSync2(authFile)) {
|
|
387
|
-
try {
|
|
388
|
-
authData = JSON.parse(readFileSync2(authFile, "utf-8"));
|
|
389
|
-
} catch {
|
|
390
|
-
}
|
|
391
|
-
}
|
|
392
|
-
authData.profiles = authData.profiles || {};
|
|
393
|
-
authData.profiles["github-copilot:github"] = {
|
|
394
|
-
type: "token",
|
|
395
|
-
provider: "github-copilot",
|
|
396
|
-
token
|
|
397
|
-
};
|
|
398
|
-
writeFileSync2(authFile, JSON.stringify(authData, null, 2) + "\n");
|
|
399
|
-
const { file } = openclawConfigPath();
|
|
400
|
-
if (existsSync2(file)) {
|
|
401
|
-
try {
|
|
402
|
-
const config = JSON.parse(readFileSync2(file, "utf-8"));
|
|
403
|
-
if (!config.auth) config.auth = {};
|
|
404
|
-
if (!config.auth.profiles) config.auth.profiles = {};
|
|
405
|
-
config.auth.profiles["github-copilot:github"] = {
|
|
406
|
-
provider: "github-copilot",
|
|
407
|
-
mode: "token"
|
|
408
|
-
};
|
|
409
|
-
writeFileSync2(file, JSON.stringify(config, null, 2) + "\n");
|
|
410
|
-
} catch {
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
function getGitHubToken(config) {
|
|
415
|
-
const configProfiles = config?.auth?.profiles;
|
|
416
|
-
if (configProfiles && typeof configProfiles === "object") {
|
|
417
|
-
for (const profile of Object.values(configProfiles)) {
|
|
418
|
-
if (profile?.provider === "github-copilot" && profile?.token) {
|
|
419
|
-
return profile.token;
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
}
|
|
423
|
-
const authFile = join2(homedir2(), ".openclaw", "agents", "main", "agent", "auth-profiles.json");
|
|
424
|
-
if (existsSync2(authFile)) {
|
|
425
|
-
try {
|
|
426
|
-
const authData = JSON.parse(readFileSync2(authFile, "utf-8"));
|
|
427
|
-
const profiles = authData?.profiles;
|
|
428
|
-
if (profiles && typeof profiles === "object") {
|
|
429
|
-
for (const profile of Object.values(profiles)) {
|
|
430
|
-
if (profile?.provider === "github-copilot" && profile?.token) {
|
|
431
|
-
return profile.token;
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
} catch {
|
|
436
|
-
}
|
|
437
|
-
}
|
|
438
|
-
return process.env.COPILOT_GITHUB_TOKEN || process.env.GH_TOKEN || process.env.GITHUB_TOKEN || null;
|
|
439
|
-
}
|
|
440
|
-
async function checkModelConfig() {
|
|
441
|
-
const { file } = openclawConfigPath();
|
|
442
|
-
if (!existsSync2(file)) return false;
|
|
443
|
-
let config;
|
|
444
|
-
try {
|
|
445
|
-
config = JSON.parse(readFileSync2(file, "utf-8"));
|
|
446
|
-
} catch {
|
|
447
|
-
return false;
|
|
448
|
-
}
|
|
449
|
-
const primary = config?.agents?.defaults?.model?.primary;
|
|
450
|
-
if (typeof primary === "string" && primary.length > 0) {
|
|
451
|
-
console.log(` Model: ${primary}`);
|
|
452
|
-
return true;
|
|
453
|
-
}
|
|
454
|
-
let githubToken = getGitHubToken(config);
|
|
455
|
-
if (!githubToken) {
|
|
456
|
-
try {
|
|
457
|
-
const accessToken = await githubDeviceAuth();
|
|
458
|
-
saveGitHubToken(accessToken);
|
|
459
|
-
githubToken = accessToken;
|
|
460
|
-
config = JSON.parse(readFileSync2(file, "utf-8"));
|
|
461
|
-
} catch (err) {
|
|
462
|
-
console.error(` GitHub auth failed: ${err.message}`);
|
|
463
|
-
console.warn(" You can retry or configure manually: openclaw onboard");
|
|
464
|
-
console.warn("");
|
|
465
|
-
return false;
|
|
466
|
-
}
|
|
467
|
-
}
|
|
468
|
-
console.log("");
|
|
469
|
-
console.log("Fetching available models from GitHub Copilot...");
|
|
470
|
-
const models = await fetchCopilotModels(githubToken);
|
|
471
|
-
console.log("");
|
|
472
|
-
console.log("Select a model:");
|
|
473
|
-
console.log("");
|
|
474
|
-
for (let i = 0; i < models.length; i++) {
|
|
475
|
-
console.log(` ${i + 1}) ${models[i].display}`);
|
|
476
|
-
}
|
|
477
|
-
console.log(` 0) Skip (configure later with: openclaw onboard)`);
|
|
478
|
-
console.log("");
|
|
479
|
-
const choice = await promptChoice(models.length);
|
|
480
|
-
if (choice === 0) {
|
|
481
|
-
console.warn("Skipped. Gateway will start but cannot respond to messages.");
|
|
482
|
-
return false;
|
|
483
|
-
}
|
|
484
|
-
const selected = models[choice - 1];
|
|
485
|
-
if (!config.agents) config.agents = {};
|
|
486
|
-
if (!config.agents.defaults) config.agents.defaults = {};
|
|
487
|
-
config.agents.defaults.model = { primary: selected.model };
|
|
488
|
-
writeFileSync2(file, JSON.stringify(config, null, 2) + "\n");
|
|
489
|
-
console.log(` Model set to: ${selected.model}`);
|
|
490
|
-
console.log("");
|
|
491
|
-
return true;
|
|
492
|
-
}
|
|
493
|
-
function promptChoice(max) {
|
|
494
|
-
return new Promise((resolve2) => {
|
|
495
|
-
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
496
|
-
rl.question(` Choose [0-${max}]: `, (answer) => {
|
|
497
|
-
rl.close();
|
|
498
|
-
const num = parseInt(answer.trim(), 10);
|
|
499
|
-
if (isNaN(num) || num < 0 || num > max) {
|
|
500
|
-
resolve2(0);
|
|
501
|
-
} else {
|
|
502
|
-
resolve2(num);
|
|
503
|
-
}
|
|
504
|
-
});
|
|
505
|
-
});
|
|
506
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
HubConnection,
|
|
3
|
+
SessionManager,
|
|
4
|
+
loadConfig,
|
|
5
|
+
saveConfig,
|
|
6
|
+
startAgent
|
|
7
|
+
} from "./chunk-YBQQZNRQ.js";
|
|
507
8
|
|
|
508
|
-
// src/
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
});
|
|
517
|
-
if (!config.token) {
|
|
518
|
-
console.error("Error: --token is required.");
|
|
519
|
-
console.error("");
|
|
520
|
-
console.error("Get your token from the Hub dashboard:");
|
|
521
|
-
console.error(" 1. Log in to the Hub");
|
|
522
|
-
console.error(" 2. Create a new instance");
|
|
523
|
-
console.error(" 3. Copy the instance token (clw_xxx)");
|
|
524
|
-
console.error("");
|
|
525
|
-
console.error("Usage: clawnet-agent start --token clw_xxx");
|
|
526
|
-
process.exit(1);
|
|
527
|
-
}
|
|
528
|
-
console.log("ClawNet Agent starting...");
|
|
529
|
-
console.log(` Hub: ${config.hubUrl}`);
|
|
530
|
-
console.log(` Port: ${config.port}`);
|
|
531
|
-
console.log(` Name: ${config.name}`);
|
|
532
|
-
console.log("");
|
|
533
|
-
const gatewayToken = ensureGatewayToken();
|
|
534
|
-
await checkModelConfig();
|
|
535
|
-
let gateway = null;
|
|
536
|
-
try {
|
|
537
|
-
console.log(`Starting OpenClaw gateway on port ${config.port}...`);
|
|
538
|
-
gateway = await launchGateway(config.port);
|
|
539
|
-
console.log(`OpenClaw gateway ready on http://localhost:${config.port}`);
|
|
540
|
-
console.log(` Dashboard: http://localhost:${config.port}/?token=${gatewayToken}`);
|
|
541
|
-
} catch (err) {
|
|
542
|
-
console.warn(
|
|
543
|
-
`Warning: Could not start OpenClaw gateway: ${err.message}`
|
|
544
|
-
);
|
|
545
|
-
console.warn("Continuing with Hub connection only...");
|
|
546
|
-
}
|
|
547
|
-
console.log(`Connecting to Hub at ${config.hubUrl}...`);
|
|
548
|
-
const hubConnection = new HubConnection({
|
|
549
|
-
hubUrl: config.hubUrl,
|
|
550
|
-
token: config.token,
|
|
551
|
-
onConnect: () => {
|
|
552
|
-
console.log("[clawnet] Connected to Hub");
|
|
553
|
-
},
|
|
554
|
-
onDisconnect: (code, reason) => {
|
|
555
|
-
console.log(`[clawnet] Disconnected from Hub: ${code} ${reason}`);
|
|
556
|
-
},
|
|
557
|
-
onError: (err) => {
|
|
558
|
-
console.error(`[clawnet] Hub connection error: ${err.message}`);
|
|
559
|
-
}
|
|
560
|
-
});
|
|
561
|
-
hubConnection.connect();
|
|
562
|
-
currentAgent = { config, gateway, hubConnection };
|
|
563
|
-
const shutdown = async () => {
|
|
564
|
-
console.log("\nShutting down...");
|
|
565
|
-
hubConnection.destroy();
|
|
566
|
-
if (gateway) {
|
|
567
|
-
await gateway.close();
|
|
568
|
-
}
|
|
569
|
-
console.log("ClawNet Agent stopped.");
|
|
570
|
-
process.exit(0);
|
|
571
|
-
};
|
|
572
|
-
process.on("SIGINT", shutdown);
|
|
573
|
-
process.on("SIGTERM", shutdown);
|
|
574
|
-
console.log("");
|
|
575
|
-
console.log("ClawNet Agent is running. Press Ctrl+C to stop.");
|
|
576
|
-
await new Promise(() => {
|
|
577
|
-
});
|
|
578
|
-
return currentAgent;
|
|
579
|
-
}
|
|
580
|
-
function showStatus() {
|
|
581
|
-
if (currentAgent) {
|
|
582
|
-
console.log("ClawNet Agent is running.");
|
|
583
|
-
console.log(` Hub: ${currentAgent.config.hubUrl}`);
|
|
584
|
-
console.log(` Connected: ${currentAgent.hubConnection.isConnected}`);
|
|
585
|
-
} else {
|
|
586
|
-
console.log("ClawNet Agent is not running in this process.");
|
|
587
|
-
console.log("Use 'clawnet-agent start' to start it.");
|
|
588
|
-
}
|
|
589
|
-
}
|
|
9
|
+
// src/index.ts
|
|
10
|
+
import {
|
|
11
|
+
SwarmCoordinator,
|
|
12
|
+
loadRole,
|
|
13
|
+
listRoles,
|
|
14
|
+
initRoles,
|
|
15
|
+
buildRolePrompt
|
|
16
|
+
} from "@mclawnet/swarm";
|
|
590
17
|
export {
|
|
591
18
|
HubConnection,
|
|
592
|
-
|
|
593
|
-
|
|
19
|
+
SessionManager,
|
|
20
|
+
SwarmCoordinator,
|
|
21
|
+
buildRolePrompt,
|
|
22
|
+
initRoles,
|
|
23
|
+
listRoles,
|
|
594
24
|
loadConfig,
|
|
25
|
+
loadRole,
|
|
595
26
|
saveConfig,
|
|
596
|
-
showStatus,
|
|
597
27
|
startAgent
|
|
598
28
|
};
|
|
29
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export { HubConnection, type HubConnectionOptions } from \"./hub-connection.js\";\nexport { SessionManager } from \"./session-manager.js\";\nexport {\n type BackendAdapter,\n type BackendProcess,\n type SpawnOptions,\n} from \"./backend-adapter.js\";\nexport { loadConfig, saveConfig, type AgentConfig } from \"./config.js\";\nexport { startAgent, type StartOptions } from \"./start.js\";\n\n// Re-export from @mclawnet/swarm for convenience\nexport {\n SwarmCoordinator,\n type RoleDefinition,\n loadRole,\n listRoles,\n initRoles,\n buildRolePrompt,\n} from \"@mclawnet/swarm\";\n"],"mappings":";;;;;;;;;AAWA;AAAA,EACE;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;","names":[]}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { BackendAdapter, SpawnOptions } from "./backend-adapter.js";
|
|
2
|
+
/**
|
|
3
|
+
* Manages active sessions, mapping ClawNet session IDs to BackendProcess instances.
|
|
4
|
+
*/
|
|
5
|
+
export declare class SessionManager {
|
|
6
|
+
private sessions;
|
|
7
|
+
private adapter;
|
|
8
|
+
private onOutput;
|
|
9
|
+
private onTurnComplete;
|
|
10
|
+
private onSessionError;
|
|
11
|
+
constructor(options: {
|
|
12
|
+
adapter: BackendAdapter;
|
|
13
|
+
onOutput: (sessionId: string, data: unknown) => void;
|
|
14
|
+
onTurnComplete: (sessionId: string, info: {
|
|
15
|
+
claudeSessionId?: string;
|
|
16
|
+
cost?: number;
|
|
17
|
+
duration?: number;
|
|
18
|
+
contextUsage?: {
|
|
19
|
+
used: number;
|
|
20
|
+
total: number;
|
|
21
|
+
};
|
|
22
|
+
}) => void;
|
|
23
|
+
onSessionError: (sessionId: string, error: string) => void;
|
|
24
|
+
});
|
|
25
|
+
createSession(options: SpawnOptions): Promise<string>;
|
|
26
|
+
sendInput(sessionId: string, input: string): void;
|
|
27
|
+
abortSession(sessionId: string): Promise<void>;
|
|
28
|
+
closeSession(sessionId: string): Promise<void>;
|
|
29
|
+
closeAll(): Promise<void>;
|
|
30
|
+
hasSession(sessionId: string): boolean;
|
|
31
|
+
get activeSessionCount(): number;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=session-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-manager.d.ts","sourceRoot":"","sources":["../src/session-manager.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAkB,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEzF;;GAEG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAqC;IACrD,OAAO,CAAC,OAAO,CAAiB;IAChC,OAAO,CAAC,QAAQ,CAA6C;IAC7D,OAAO,CAAC,cAAc,CAQZ;IACV,OAAO,CAAC,cAAc,CAA6C;gBAEvD,OAAO,EAAE;QACnB,OAAO,EAAE,cAAc,CAAC;QACxB,QAAQ,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;QACrD,cAAc,EAAE,CACd,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE;YACJ,eAAe,CAAC,EAAE,MAAM,CAAC;YACzB,IAAI,CAAC,EAAE,MAAM,CAAC;YACd,QAAQ,CAAC,EAAE,MAAM,CAAC;YAClB,YAAY,CAAC,EAAE;gBAAE,IAAI,EAAE,MAAM,CAAC;gBAAC,KAAK,EAAE,MAAM,CAAA;aAAE,CAAC;SAChD,KACE,IAAI,CAAC;QACV,cAAc,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;KAC5D;IAOK,aAAa,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAgC3D,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAS3C,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS9C,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO9C,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAU/B,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO;IAItC,IAAI,kBAAkB,IAAI,MAAM,CAE/B;CACF"}
|