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