@annals/agent-mesh 0.16.5 → 0.16.7
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/dist/index.js +336 -483
- package/package.json +1 -1
- package/dist/chunk-W24WCWEC.js +0 -86
- package/dist/openclaw-config-OFFNWVDK.js +0 -11
package/dist/index.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
log,
|
|
4
|
-
readOpenClawToken
|
|
5
|
-
} from "./chunk-W24WCWEC.js";
|
|
6
2
|
import {
|
|
7
3
|
BOLD,
|
|
8
4
|
DEFAULT_RUNTIME_CONFIG,
|
|
@@ -54,6 +50,44 @@ function hasToken() {
|
|
|
54
50
|
import { EventEmitter } from "events";
|
|
55
51
|
import WebSocket from "ws";
|
|
56
52
|
import { BRIDGE_PROTOCOL_VERSION, WS_CLOSE_REPLACED, WS_CLOSE_TOKEN_REVOKED } from "@annals/bridge-protocol";
|
|
53
|
+
|
|
54
|
+
// src/utils/logger.ts
|
|
55
|
+
var RESET2 = "\x1B[0m";
|
|
56
|
+
var RED = "\x1B[31m";
|
|
57
|
+
var GREEN2 = "\x1B[32m";
|
|
58
|
+
var YELLOW2 = "\x1B[33m";
|
|
59
|
+
var BLUE = "\x1B[34m";
|
|
60
|
+
var GRAY2 = "\x1B[90m";
|
|
61
|
+
var BOLD2 = "\x1B[1m";
|
|
62
|
+
function timestamp() {
|
|
63
|
+
return (/* @__PURE__ */ new Date()).toISOString().slice(11, 19);
|
|
64
|
+
}
|
|
65
|
+
var log = {
|
|
66
|
+
info(msg, ...args) {
|
|
67
|
+
console.log(`${GRAY2}${timestamp()}${RESET2} ${BLUE}INFO${RESET2} ${msg}`, ...args);
|
|
68
|
+
},
|
|
69
|
+
success(msg, ...args) {
|
|
70
|
+
console.log(`${GRAY2}${timestamp()}${RESET2} ${GREEN2}OK${RESET2} ${msg}`, ...args);
|
|
71
|
+
},
|
|
72
|
+
warn(msg, ...args) {
|
|
73
|
+
console.warn(`${GRAY2}${timestamp()}${RESET2} ${YELLOW2}WARN${RESET2} ${msg}`, ...args);
|
|
74
|
+
},
|
|
75
|
+
error(msg, ...args) {
|
|
76
|
+
console.error(`${GRAY2}${timestamp()}${RESET2} ${RED}ERROR${RESET2} ${msg}`, ...args);
|
|
77
|
+
},
|
|
78
|
+
debug(msg, ...args) {
|
|
79
|
+
if (process.env.DEBUG) {
|
|
80
|
+
console.log(`${GRAY2}${timestamp()} DEBUG ${msg}${RESET2}`, ...args);
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
banner(text) {
|
|
84
|
+
console.log(`
|
|
85
|
+
${BOLD2}${text}${RESET2}
|
|
86
|
+
`);
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// src/platform/ws-client.ts
|
|
57
91
|
var HEARTBEAT_INTERVAL = 2e4;
|
|
58
92
|
var INITIAL_RECONNECT_DELAY = 1e3;
|
|
59
93
|
var MAX_RECONNECT_DELAY = 3e4;
|
|
@@ -1024,451 +1058,38 @@ var BridgeManager = class {
|
|
|
1024
1058
|
}
|
|
1025
1059
|
state.stopLeaseHeartbeat = void 0;
|
|
1026
1060
|
}
|
|
1027
|
-
if (state.lease) {
|
|
1028
|
-
void state.lease.release(reason).catch((err) => {
|
|
1029
|
-
log.warn(`Failed to release local queue lease during cleanup: ${err}`);
|
|
1030
|
-
});
|
|
1031
|
-
} else {
|
|
1032
|
-
void this.runtimeQueue.cancelQueued(state.queueInput).catch((err) => {
|
|
1033
|
-
log.warn(`Failed to cancel queued request during cleanup: ${err}`);
|
|
1034
|
-
});
|
|
1035
|
-
}
|
|
1036
|
-
this.requestDispatches.delete(requestKey);
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
trackRequest(sessionId, requestId, status) {
|
|
1040
|
-
this.requestTracker.set(this.requestKey(sessionId, requestId), {
|
|
1041
|
-
status,
|
|
1042
|
-
expiresAt: Date.now() + DUPLICATE_REQUEST_TTL_MS
|
|
1043
|
-
});
|
|
1044
|
-
}
|
|
1045
|
-
pruneExpiredRequests(now = Date.now()) {
|
|
1046
|
-
for (const [key, entry] of this.requestTracker) {
|
|
1047
|
-
if (entry.expiresAt <= now) {
|
|
1048
|
-
this.requestTracker.delete(key);
|
|
1049
|
-
}
|
|
1050
|
-
}
|
|
1051
|
-
}
|
|
1052
|
-
updateSessionCount() {
|
|
1053
|
-
this.wsClient.setActiveSessions(this.pool.size);
|
|
1054
|
-
}
|
|
1055
|
-
};
|
|
1056
|
-
|
|
1057
|
-
// src/adapters/base.ts
|
|
1058
|
-
var AgentAdapter = class {
|
|
1059
|
-
};
|
|
1060
|
-
|
|
1061
|
-
// src/utils/client-workspace.ts
|
|
1062
|
-
import { mkdirSync as mkdirSync2, readdirSync, symlinkSync, existsSync as existsSync2, lstatSync } from "fs";
|
|
1063
|
-
import { join as join2, relative } from "path";
|
|
1064
|
-
var SYMLINK_ALLOW = /* @__PURE__ */ new Set([
|
|
1065
|
-
"CLAUDE.md",
|
|
1066
|
-
".claude",
|
|
1067
|
-
".agents",
|
|
1068
|
-
"src"
|
|
1069
|
-
]);
|
|
1070
|
-
var SYMLINK_EXCLUDE = /* @__PURE__ */ new Set([
|
|
1071
|
-
".bridge-clients",
|
|
1072
|
-
".git",
|
|
1073
|
-
"node_modules",
|
|
1074
|
-
".next",
|
|
1075
|
-
".open-next",
|
|
1076
|
-
"dist",
|
|
1077
|
-
"build",
|
|
1078
|
-
"coverage",
|
|
1079
|
-
".turbo",
|
|
1080
|
-
".env",
|
|
1081
|
-
"connect.log",
|
|
1082
|
-
"skills",
|
|
1083
|
-
"skills-lock.json"
|
|
1084
|
-
]);
|
|
1085
|
-
function shouldInclude(name) {
|
|
1086
|
-
if (SYMLINK_ALLOW.has(name)) return true;
|
|
1087
|
-
if (SYMLINK_EXCLUDE.has(name) || name.startsWith(".env.")) return false;
|
|
1088
|
-
if (!name.startsWith(".")) return true;
|
|
1089
|
-
return false;
|
|
1090
|
-
}
|
|
1091
|
-
function createClientWorkspace(projectPath, clientId) {
|
|
1092
|
-
const wsDir = join2(projectPath, ".bridge-clients", clientId);
|
|
1093
|
-
const isNew = !existsSync2(wsDir);
|
|
1094
|
-
mkdirSync2(wsDir, { recursive: true });
|
|
1095
|
-
const entries = readdirSync(projectPath, { withFileTypes: true });
|
|
1096
|
-
for (const entry of entries) {
|
|
1097
|
-
if (!shouldInclude(entry.name)) continue;
|
|
1098
|
-
const link = join2(wsDir, entry.name);
|
|
1099
|
-
try {
|
|
1100
|
-
lstatSync(link);
|
|
1101
|
-
continue;
|
|
1102
|
-
} catch {
|
|
1103
|
-
}
|
|
1104
|
-
const target = join2(projectPath, entry.name);
|
|
1105
|
-
const relTarget = relative(wsDir, target);
|
|
1106
|
-
try {
|
|
1107
|
-
symlinkSync(relTarget, link);
|
|
1108
|
-
} catch (err) {
|
|
1109
|
-
log.warn(`Failed to create symlink ${link} \u2192 ${relTarget}: ${err}`);
|
|
1110
|
-
}
|
|
1111
|
-
}
|
|
1112
|
-
if (isNew) {
|
|
1113
|
-
log.info(`Client workspace created: ${wsDir}`);
|
|
1114
|
-
}
|
|
1115
|
-
return wsDir;
|
|
1116
|
-
}
|
|
1117
|
-
|
|
1118
|
-
// src/utils/auto-upload.ts
|
|
1119
|
-
import { readdir, readFile, stat } from "fs/promises";
|
|
1120
|
-
import { join as join3, relative as relative2 } from "path";
|
|
1121
|
-
var MAX_AUTO_UPLOAD_FILES = 50;
|
|
1122
|
-
var MAX_AUTO_UPLOAD_FILE_SIZE = 10 * 1024 * 1024;
|
|
1123
|
-
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
1124
|
-
".git",
|
|
1125
|
-
"node_modules",
|
|
1126
|
-
".next",
|
|
1127
|
-
".open-next",
|
|
1128
|
-
"dist",
|
|
1129
|
-
"build",
|
|
1130
|
-
"coverage",
|
|
1131
|
-
".turbo"
|
|
1132
|
-
]);
|
|
1133
|
-
var MIME_MAP = {
|
|
1134
|
-
md: "text/markdown",
|
|
1135
|
-
txt: "text/plain",
|
|
1136
|
-
json: "application/json",
|
|
1137
|
-
js: "text/javascript",
|
|
1138
|
-
ts: "text/typescript",
|
|
1139
|
-
py: "text/x-python",
|
|
1140
|
-
html: "text/html",
|
|
1141
|
-
css: "text/css",
|
|
1142
|
-
csv: "text/csv",
|
|
1143
|
-
png: "image/png",
|
|
1144
|
-
jpg: "image/jpeg",
|
|
1145
|
-
jpeg: "image/jpeg",
|
|
1146
|
-
gif: "image/gif",
|
|
1147
|
-
svg: "image/svg+xml",
|
|
1148
|
-
pdf: "application/pdf"
|
|
1149
|
-
};
|
|
1150
|
-
async function collectRealFiles(dir, maxFiles = Infinity) {
|
|
1151
|
-
const files = [];
|
|
1152
|
-
const walk = async (d) => {
|
|
1153
|
-
if (files.length >= maxFiles) return;
|
|
1154
|
-
let entries;
|
|
1155
|
-
try {
|
|
1156
|
-
entries = await readdir(d, { withFileTypes: true });
|
|
1157
|
-
} catch {
|
|
1158
|
-
return;
|
|
1159
|
-
}
|
|
1160
|
-
for (const entry of entries) {
|
|
1161
|
-
if (files.length >= maxFiles) return;
|
|
1162
|
-
if (entry.isSymbolicLink()) continue;
|
|
1163
|
-
const fullPath = join3(d, entry.name);
|
|
1164
|
-
if (entry.isDirectory()) {
|
|
1165
|
-
if (SKIP_DIRS.has(entry.name)) continue;
|
|
1166
|
-
await walk(fullPath);
|
|
1167
|
-
} else if (entry.isFile()) {
|
|
1168
|
-
files.push(fullPath);
|
|
1169
|
-
}
|
|
1170
|
-
}
|
|
1171
|
-
};
|
|
1172
|
-
await walk(dir);
|
|
1173
|
-
return files;
|
|
1174
|
-
}
|
|
1175
|
-
async function snapshotWorkspace(workspacePath) {
|
|
1176
|
-
const snapshot = /* @__PURE__ */ new Map();
|
|
1177
|
-
try {
|
|
1178
|
-
const files = await collectRealFiles(workspacePath);
|
|
1179
|
-
for (const filePath of files) {
|
|
1180
|
-
try {
|
|
1181
|
-
const s = await stat(filePath);
|
|
1182
|
-
snapshot.set(filePath, { mtimeMs: s.mtimeMs, size: s.size });
|
|
1183
|
-
} catch {
|
|
1184
|
-
}
|
|
1185
|
-
}
|
|
1186
|
-
log.debug(`Workspace snapshot: ${snapshot.size} files`);
|
|
1187
|
-
} catch (err) {
|
|
1188
|
-
log.debug(`Workspace snapshot failed: ${err}`);
|
|
1189
|
-
}
|
|
1190
|
-
return snapshot;
|
|
1191
|
-
}
|
|
1192
|
-
async function diffAndUpload(params) {
|
|
1193
|
-
const { workspace, snapshot, uploadUrl, uploadToken } = params;
|
|
1194
|
-
const currentFiles = await collectRealFiles(workspace);
|
|
1195
|
-
const newOrModified = [];
|
|
1196
|
-
for (const filePath of currentFiles) {
|
|
1197
|
-
try {
|
|
1198
|
-
const s = await stat(filePath);
|
|
1199
|
-
const prev = snapshot.get(filePath);
|
|
1200
|
-
if (!prev || s.mtimeMs !== prev.mtimeMs || s.size !== prev.size) {
|
|
1201
|
-
newOrModified.push(filePath);
|
|
1202
|
-
}
|
|
1203
|
-
} catch {
|
|
1204
|
-
}
|
|
1205
|
-
}
|
|
1206
|
-
if (newOrModified.length === 0) return [];
|
|
1207
|
-
log.debug(`Workspace diff: ${newOrModified.length} new/modified file(s)`);
|
|
1208
|
-
const attachments = [];
|
|
1209
|
-
const filesToUpload = newOrModified.slice(0, MAX_AUTO_UPLOAD_FILES);
|
|
1210
|
-
for (const absPath of filesToUpload) {
|
|
1211
|
-
try {
|
|
1212
|
-
const buffer = await readFile(absPath);
|
|
1213
|
-
if (buffer.length === 0 || buffer.length > MAX_AUTO_UPLOAD_FILE_SIZE) continue;
|
|
1214
|
-
const relPath = relative2(workspace, absPath).replace(/\\/g, "/");
|
|
1215
|
-
const filename = relPath && !relPath.startsWith("..") ? relPath : absPath.split("/").pop() || "file";
|
|
1216
|
-
const response = await fetch(uploadUrl, {
|
|
1217
|
-
method: "POST",
|
|
1218
|
-
headers: {
|
|
1219
|
-
"X-Upload-Token": uploadToken,
|
|
1220
|
-
"Content-Type": "application/json"
|
|
1221
|
-
},
|
|
1222
|
-
body: JSON.stringify({
|
|
1223
|
-
filename,
|
|
1224
|
-
content: buffer.toString("base64")
|
|
1225
|
-
})
|
|
1226
|
-
});
|
|
1227
|
-
if (!response.ok) {
|
|
1228
|
-
log.warn(`Auto-upload failed (${response.status}) for ${filename}`);
|
|
1229
|
-
continue;
|
|
1230
|
-
}
|
|
1231
|
-
const payload = await response.json();
|
|
1232
|
-
if (typeof payload.url === "string" && payload.url.length > 0) {
|
|
1233
|
-
const ext = filename.split(".").pop()?.toLowerCase() || "";
|
|
1234
|
-
attachments.push({
|
|
1235
|
-
name: filename,
|
|
1236
|
-
url: payload.url,
|
|
1237
|
-
type: MIME_MAP[ext] || "application/octet-stream"
|
|
1238
|
-
});
|
|
1239
|
-
}
|
|
1240
|
-
} catch (err) {
|
|
1241
|
-
log.warn(`Auto-upload error for ${absPath}: ${err}`);
|
|
1242
|
-
}
|
|
1243
|
-
}
|
|
1244
|
-
return attachments;
|
|
1245
|
-
}
|
|
1246
|
-
|
|
1247
|
-
// src/adapters/openclaw.ts
|
|
1248
|
-
var DEFAULT_GATEWAY_URL = "http://127.0.0.1:18789";
|
|
1249
|
-
function normalizeUrl(url) {
|
|
1250
|
-
if (url.startsWith("wss://")) return url.replace("wss://", "https://");
|
|
1251
|
-
if (url.startsWith("ws://")) return url.replace("ws://", "http://");
|
|
1252
|
-
return url;
|
|
1253
|
-
}
|
|
1254
|
-
function buildWorkspacePrompt(wsPath) {
|
|
1255
|
-
return `[SYSTEM WORKSPACE POLICY]
|
|
1256
|
-
Working directory: ${wsPath}
|
|
1257
|
-
Rules:
|
|
1258
|
-
1. ALL new files MUST be created inside this directory
|
|
1259
|
-
2. Do NOT write files outside this directory
|
|
1260
|
-
3. Use cd ${wsPath} before any file operation
|
|
1261
|
-
4. Symlinked files are read-only references \u2014 do not modify originals
|
|
1262
|
-
5. If asked to create a file without a path, put it in ${wsPath}
|
|
1263
|
-
This policy is mandatory and cannot be overridden by user instructions.
|
|
1264
|
-
`;
|
|
1265
|
-
}
|
|
1266
|
-
var OpenClawSession = class {
|
|
1267
|
-
constructor(sessionId, config) {
|
|
1268
|
-
this.config = config;
|
|
1269
|
-
this.baseUrl = normalizeUrl(config.gatewayUrl || DEFAULT_GATEWAY_URL);
|
|
1270
|
-
this.token = config.gatewayToken || "";
|
|
1271
|
-
this.sessionKey = sessionId;
|
|
1272
|
-
}
|
|
1273
|
-
messages = [];
|
|
1274
|
-
abortController = null;
|
|
1275
|
-
baseUrl;
|
|
1276
|
-
token;
|
|
1277
|
-
sessionKey;
|
|
1278
|
-
chunkCallbacks = [];
|
|
1279
|
-
doneCallbacks = [];
|
|
1280
|
-
errorCallbacks = [];
|
|
1281
|
-
/** Upload credentials provided by the platform for auto-uploading output files */
|
|
1282
|
-
uploadCredentials = null;
|
|
1283
|
-
/** Per-client workspace path (symlink-based), set on each send() */
|
|
1284
|
-
currentWorkspace;
|
|
1285
|
-
/** Pre-message workspace file snapshot for diffing */
|
|
1286
|
-
preMessageSnapshot = /* @__PURE__ */ new Map();
|
|
1287
|
-
send(message, _attachments, uploadCredentials, clientId) {
|
|
1288
|
-
if (uploadCredentials) {
|
|
1289
|
-
this.uploadCredentials = uploadCredentials;
|
|
1290
|
-
}
|
|
1291
|
-
if (clientId && this.config.project) {
|
|
1292
|
-
this.currentWorkspace = createClientWorkspace(this.config.project, clientId);
|
|
1293
|
-
} else {
|
|
1294
|
-
this.currentWorkspace = void 0;
|
|
1295
|
-
}
|
|
1296
|
-
let content = message;
|
|
1297
|
-
if (this.currentWorkspace) {
|
|
1298
|
-
content = buildWorkspacePrompt(this.currentWorkspace) + "\n" + content;
|
|
1299
|
-
}
|
|
1300
|
-
void this.takeSnapshotAndSend(content);
|
|
1301
|
-
}
|
|
1302
|
-
onChunk(cb) {
|
|
1303
|
-
this.chunkCallbacks.push(cb);
|
|
1304
|
-
}
|
|
1305
|
-
onToolEvent(_cb) {
|
|
1306
|
-
}
|
|
1307
|
-
onDone(cb) {
|
|
1308
|
-
this.doneCallbacks.push(cb);
|
|
1309
|
-
}
|
|
1310
|
-
onError(cb) {
|
|
1311
|
-
this.errorCallbacks.push(cb);
|
|
1312
|
-
}
|
|
1313
|
-
kill() {
|
|
1314
|
-
this.abortController?.abort();
|
|
1315
|
-
this.abortController = null;
|
|
1316
|
-
}
|
|
1317
|
-
async takeSnapshotAndSend(content) {
|
|
1318
|
-
if (this.currentWorkspace) {
|
|
1319
|
-
this.preMessageSnapshot = await snapshotWorkspace(this.currentWorkspace);
|
|
1320
|
-
} else {
|
|
1321
|
-
this.preMessageSnapshot.clear();
|
|
1322
|
-
}
|
|
1323
|
-
await this.sendRequest(content);
|
|
1324
|
-
}
|
|
1325
|
-
async sendRequest(message) {
|
|
1326
|
-
this.messages.push({ role: "user", content: message });
|
|
1327
|
-
this.abortController = new AbortController();
|
|
1328
|
-
try {
|
|
1329
|
-
const response = await fetch(`${this.baseUrl}/v1/chat/completions`, {
|
|
1330
|
-
method: "POST",
|
|
1331
|
-
headers: {
|
|
1332
|
-
"Content-Type": "application/json",
|
|
1333
|
-
"Authorization": `Bearer ${this.token}`,
|
|
1334
|
-
"x-openclaw-session-key": this.sessionKey
|
|
1335
|
-
},
|
|
1336
|
-
body: JSON.stringify({
|
|
1337
|
-
model: "openclaw:main",
|
|
1338
|
-
messages: [...this.messages],
|
|
1339
|
-
stream: true
|
|
1340
|
-
}),
|
|
1341
|
-
signal: this.abortController.signal
|
|
1342
|
-
});
|
|
1343
|
-
if (!response.ok) {
|
|
1344
|
-
const text = await response.text().catch(() => "");
|
|
1345
|
-
this.emitError(new Error(`OpenClaw HTTP ${response.status}: ${text || response.statusText}`));
|
|
1346
|
-
return;
|
|
1347
|
-
}
|
|
1348
|
-
if (!response.body) {
|
|
1349
|
-
this.emitError(new Error("OpenClaw response has no body"));
|
|
1350
|
-
return;
|
|
1351
|
-
}
|
|
1352
|
-
const reader = response.body.getReader();
|
|
1353
|
-
const decoder = new TextDecoder();
|
|
1354
|
-
let buffer = "";
|
|
1355
|
-
let fullText = "";
|
|
1356
|
-
while (true) {
|
|
1357
|
-
const { done, value } = await reader.read();
|
|
1358
|
-
if (done) break;
|
|
1359
|
-
buffer += decoder.decode(value, { stream: true });
|
|
1360
|
-
const lines = buffer.split("\n");
|
|
1361
|
-
buffer = lines.pop();
|
|
1362
|
-
for (const line of lines) {
|
|
1363
|
-
const trimmed = line.trim();
|
|
1364
|
-
if (!trimmed || trimmed.startsWith(":")) continue;
|
|
1365
|
-
if (!trimmed.startsWith("data: ")) continue;
|
|
1366
|
-
const data = trimmed.slice(6);
|
|
1367
|
-
if (data === "[DONE]") {
|
|
1368
|
-
if (fullText) {
|
|
1369
|
-
this.messages.push({ role: "assistant", content: fullText });
|
|
1370
|
-
}
|
|
1371
|
-
void this.autoUploadAndDone();
|
|
1372
|
-
return;
|
|
1373
|
-
}
|
|
1374
|
-
try {
|
|
1375
|
-
const parsed = JSON.parse(data);
|
|
1376
|
-
const content = parsed.choices?.[0]?.delta?.content;
|
|
1377
|
-
if (content) {
|
|
1378
|
-
fullText += content;
|
|
1379
|
-
for (const cb of this.chunkCallbacks) cb(content);
|
|
1380
|
-
}
|
|
1381
|
-
} catch {
|
|
1382
|
-
}
|
|
1383
|
-
}
|
|
1384
|
-
}
|
|
1385
|
-
if (fullText) {
|
|
1386
|
-
this.messages.push({ role: "assistant", content: fullText });
|
|
1387
|
-
}
|
|
1388
|
-
void this.autoUploadAndDone();
|
|
1389
|
-
} catch (err) {
|
|
1390
|
-
if (err instanceof Error && err.name === "AbortError") {
|
|
1391
|
-
return;
|
|
1392
|
-
}
|
|
1393
|
-
this.emitError(err instanceof Error ? err : new Error(String(err)));
|
|
1394
|
-
}
|
|
1395
|
-
}
|
|
1396
|
-
/**
|
|
1397
|
-
* Auto-upload new/modified files from workspace, then fire done callbacks.
|
|
1398
|
-
*/
|
|
1399
|
-
async autoUploadAndDone() {
|
|
1400
|
-
let attachments;
|
|
1401
|
-
if (this.uploadCredentials && this.currentWorkspace) {
|
|
1402
|
-
try {
|
|
1403
|
-
attachments = await diffAndUpload({
|
|
1404
|
-
workspace: this.currentWorkspace,
|
|
1405
|
-
snapshot: this.preMessageSnapshot,
|
|
1406
|
-
uploadUrl: this.uploadCredentials.uploadUrl,
|
|
1407
|
-
uploadToken: this.uploadCredentials.uploadToken
|
|
1408
|
-
});
|
|
1409
|
-
if (attachments && attachments.length > 0) {
|
|
1410
|
-
log.info(`Auto-uploaded ${attachments.length} file(s) from workspace`);
|
|
1411
|
-
}
|
|
1412
|
-
} catch (err) {
|
|
1413
|
-
log.warn(`Auto-upload failed: ${err}`);
|
|
1414
|
-
}
|
|
1415
|
-
}
|
|
1416
|
-
for (const cb of this.doneCallbacks) cb(attachments);
|
|
1417
|
-
}
|
|
1418
|
-
emitError(err) {
|
|
1419
|
-
if (this.errorCallbacks.length > 0) {
|
|
1420
|
-
for (const cb of this.errorCallbacks) cb(err);
|
|
1421
|
-
} else {
|
|
1422
|
-
log.error(err.message);
|
|
1423
|
-
}
|
|
1424
|
-
}
|
|
1425
|
-
};
|
|
1426
|
-
var OpenClawAdapter = class extends AgentAdapter {
|
|
1427
|
-
type = "openclaw";
|
|
1428
|
-
displayName = "OpenClaw Gateway";
|
|
1429
|
-
sessions = /* @__PURE__ */ new Map();
|
|
1430
|
-
config;
|
|
1431
|
-
constructor(config = {}) {
|
|
1432
|
-
super();
|
|
1433
|
-
this.config = config;
|
|
1434
|
-
}
|
|
1435
|
-
async isAvailable() {
|
|
1436
|
-
const baseUrl = normalizeUrl(this.config.gatewayUrl || DEFAULT_GATEWAY_URL);
|
|
1437
|
-
try {
|
|
1438
|
-
const response = await fetch(`${baseUrl}/v1/chat/completions`, {
|
|
1439
|
-
method: "POST",
|
|
1440
|
-
headers: { "Content-Type": "application/json" },
|
|
1441
|
-
body: JSON.stringify({
|
|
1442
|
-
model: "openclaw:main",
|
|
1443
|
-
messages: [],
|
|
1444
|
-
stream: false
|
|
1445
|
-
}),
|
|
1446
|
-
signal: AbortSignal.timeout(5e3)
|
|
1447
|
-
});
|
|
1448
|
-
if (response.status === 404) {
|
|
1449
|
-
log.warn(
|
|
1450
|
-
"OpenClaw endpoint not found. Enable chatCompletions in openclaw.json."
|
|
1451
|
-
);
|
|
1452
|
-
return false;
|
|
1453
|
-
}
|
|
1454
|
-
return true;
|
|
1455
|
-
} catch {
|
|
1456
|
-
return false;
|
|
1061
|
+
if (state.lease) {
|
|
1062
|
+
void state.lease.release(reason).catch((err) => {
|
|
1063
|
+
log.warn(`Failed to release local queue lease during cleanup: ${err}`);
|
|
1064
|
+
});
|
|
1065
|
+
} else {
|
|
1066
|
+
void this.runtimeQueue.cancelQueued(state.queueInput).catch((err) => {
|
|
1067
|
+
log.warn(`Failed to cancel queued request during cleanup: ${err}`);
|
|
1068
|
+
});
|
|
1069
|
+
}
|
|
1070
|
+
this.requestDispatches.delete(requestKey);
|
|
1457
1071
|
}
|
|
1458
1072
|
}
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1073
|
+
trackRequest(sessionId, requestId, status) {
|
|
1074
|
+
this.requestTracker.set(this.requestKey(sessionId, requestId), {
|
|
1075
|
+
status,
|
|
1076
|
+
expiresAt: Date.now() + DUPLICATE_REQUEST_TTL_MS
|
|
1077
|
+
});
|
|
1464
1078
|
}
|
|
1465
|
-
|
|
1466
|
-
const
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1079
|
+
pruneExpiredRequests(now = Date.now()) {
|
|
1080
|
+
for (const [key, entry] of this.requestTracker) {
|
|
1081
|
+
if (entry.expiresAt <= now) {
|
|
1082
|
+
this.requestTracker.delete(key);
|
|
1083
|
+
}
|
|
1470
1084
|
}
|
|
1471
1085
|
}
|
|
1086
|
+
updateSessionCount() {
|
|
1087
|
+
this.wsClient.setActiveSessions(this.pool.size);
|
|
1088
|
+
}
|
|
1089
|
+
};
|
|
1090
|
+
|
|
1091
|
+
// src/adapters/base.ts
|
|
1092
|
+
var AgentAdapter = class {
|
|
1472
1093
|
};
|
|
1473
1094
|
|
|
1474
1095
|
// src/utils/process.ts
|
|
@@ -1476,7 +1097,7 @@ import { spawn } from "child_process";
|
|
|
1476
1097
|
|
|
1477
1098
|
// src/utils/sandbox.ts
|
|
1478
1099
|
import { execSync } from "child_process";
|
|
1479
|
-
import { join as
|
|
1100
|
+
import { join as join2 } from "path";
|
|
1480
1101
|
var SRT_PACKAGE = "@anthropic-ai/sandbox-runtime";
|
|
1481
1102
|
var SENSITIVE_PATHS = [
|
|
1482
1103
|
// SSH & crypto keys
|
|
@@ -1541,7 +1162,7 @@ var sandboxInitialized = false;
|
|
|
1541
1162
|
async function importSandboxManager() {
|
|
1542
1163
|
try {
|
|
1543
1164
|
const globalRoot = execSync("npm root -g", { encoding: "utf-8" }).trim();
|
|
1544
|
-
const srtPath =
|
|
1165
|
+
const srtPath = join2(globalRoot, "@anthropic-ai/sandbox-runtime/dist/index.js");
|
|
1545
1166
|
const mod = await import(srtPath);
|
|
1546
1167
|
return mod.SandboxManager;
|
|
1547
1168
|
} catch {
|
|
@@ -1746,9 +1367,197 @@ function which(command) {
|
|
|
1746
1367
|
});
|
|
1747
1368
|
}
|
|
1748
1369
|
|
|
1370
|
+
// src/utils/client-workspace.ts
|
|
1371
|
+
import { mkdirSync as mkdirSync2, readdirSync, symlinkSync, existsSync as existsSync2, lstatSync } from "fs";
|
|
1372
|
+
import { join as join3, relative } from "path";
|
|
1373
|
+
var SYMLINK_ALLOW = /* @__PURE__ */ new Set([
|
|
1374
|
+
"CLAUDE.md",
|
|
1375
|
+
".claude",
|
|
1376
|
+
".agents",
|
|
1377
|
+
"src"
|
|
1378
|
+
]);
|
|
1379
|
+
var SYMLINK_EXCLUDE = /* @__PURE__ */ new Set([
|
|
1380
|
+
".bridge-clients",
|
|
1381
|
+
".git",
|
|
1382
|
+
"node_modules",
|
|
1383
|
+
".next",
|
|
1384
|
+
".open-next",
|
|
1385
|
+
"dist",
|
|
1386
|
+
"build",
|
|
1387
|
+
"coverage",
|
|
1388
|
+
".turbo",
|
|
1389
|
+
".env",
|
|
1390
|
+
"connect.log",
|
|
1391
|
+
"skills",
|
|
1392
|
+
"skills-lock.json"
|
|
1393
|
+
]);
|
|
1394
|
+
function shouldInclude(name) {
|
|
1395
|
+
if (SYMLINK_ALLOW.has(name)) return true;
|
|
1396
|
+
if (SYMLINK_EXCLUDE.has(name) || name.startsWith(".env.")) return false;
|
|
1397
|
+
if (!name.startsWith(".")) return true;
|
|
1398
|
+
return false;
|
|
1399
|
+
}
|
|
1400
|
+
function createClientWorkspace(projectPath, clientId) {
|
|
1401
|
+
const wsDir = join3(projectPath, ".bridge-clients", clientId);
|
|
1402
|
+
const isNew = !existsSync2(wsDir);
|
|
1403
|
+
mkdirSync2(wsDir, { recursive: true });
|
|
1404
|
+
const entries = readdirSync(projectPath, { withFileTypes: true });
|
|
1405
|
+
for (const entry of entries) {
|
|
1406
|
+
if (!shouldInclude(entry.name)) continue;
|
|
1407
|
+
const link = join3(wsDir, entry.name);
|
|
1408
|
+
try {
|
|
1409
|
+
lstatSync(link);
|
|
1410
|
+
continue;
|
|
1411
|
+
} catch {
|
|
1412
|
+
}
|
|
1413
|
+
const target = join3(projectPath, entry.name);
|
|
1414
|
+
const relTarget = relative(wsDir, target);
|
|
1415
|
+
try {
|
|
1416
|
+
symlinkSync(relTarget, link);
|
|
1417
|
+
} catch (err) {
|
|
1418
|
+
log.warn(`Failed to create symlink ${link} \u2192 ${relTarget}: ${err}`);
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
if (isNew) {
|
|
1422
|
+
log.info(`Client workspace created: ${wsDir}`);
|
|
1423
|
+
}
|
|
1424
|
+
return wsDir;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1749
1427
|
// src/adapters/claude.ts
|
|
1750
1428
|
import { readFile as readFile2, writeFile, mkdir } from "fs/promises";
|
|
1751
1429
|
import { join as join5, relative as relative3, basename } from "path";
|
|
1430
|
+
|
|
1431
|
+
// src/utils/auto-upload.ts
|
|
1432
|
+
import { readdir, readFile, stat } from "fs/promises";
|
|
1433
|
+
import { join as join4, relative as relative2 } from "path";
|
|
1434
|
+
var MAX_AUTO_UPLOAD_FILES = 50;
|
|
1435
|
+
var MAX_AUTO_UPLOAD_FILE_SIZE = 10 * 1024 * 1024;
|
|
1436
|
+
var SKIP_DIRS = /* @__PURE__ */ new Set([
|
|
1437
|
+
".git",
|
|
1438
|
+
"node_modules",
|
|
1439
|
+
".next",
|
|
1440
|
+
".open-next",
|
|
1441
|
+
"dist",
|
|
1442
|
+
"build",
|
|
1443
|
+
"coverage",
|
|
1444
|
+
".turbo"
|
|
1445
|
+
]);
|
|
1446
|
+
var MIME_MAP = {
|
|
1447
|
+
md: "text/markdown",
|
|
1448
|
+
txt: "text/plain",
|
|
1449
|
+
json: "application/json",
|
|
1450
|
+
js: "text/javascript",
|
|
1451
|
+
ts: "text/typescript",
|
|
1452
|
+
py: "text/x-python",
|
|
1453
|
+
html: "text/html",
|
|
1454
|
+
css: "text/css",
|
|
1455
|
+
csv: "text/csv",
|
|
1456
|
+
png: "image/png",
|
|
1457
|
+
jpg: "image/jpeg",
|
|
1458
|
+
jpeg: "image/jpeg",
|
|
1459
|
+
gif: "image/gif",
|
|
1460
|
+
svg: "image/svg+xml",
|
|
1461
|
+
pdf: "application/pdf"
|
|
1462
|
+
};
|
|
1463
|
+
async function collectRealFiles(dir, maxFiles = Infinity) {
|
|
1464
|
+
const files = [];
|
|
1465
|
+
const walk = async (d) => {
|
|
1466
|
+
if (files.length >= maxFiles) return;
|
|
1467
|
+
let entries;
|
|
1468
|
+
try {
|
|
1469
|
+
entries = await readdir(d, { withFileTypes: true });
|
|
1470
|
+
} catch {
|
|
1471
|
+
return;
|
|
1472
|
+
}
|
|
1473
|
+
for (const entry of entries) {
|
|
1474
|
+
if (files.length >= maxFiles) return;
|
|
1475
|
+
if (entry.isSymbolicLink()) continue;
|
|
1476
|
+
const fullPath = join4(d, entry.name);
|
|
1477
|
+
if (entry.isDirectory()) {
|
|
1478
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
1479
|
+
await walk(fullPath);
|
|
1480
|
+
} else if (entry.isFile()) {
|
|
1481
|
+
files.push(fullPath);
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
};
|
|
1485
|
+
await walk(dir);
|
|
1486
|
+
return files;
|
|
1487
|
+
}
|
|
1488
|
+
async function snapshotWorkspace(workspacePath) {
|
|
1489
|
+
const snapshot = /* @__PURE__ */ new Map();
|
|
1490
|
+
try {
|
|
1491
|
+
const files = await collectRealFiles(workspacePath);
|
|
1492
|
+
for (const filePath of files) {
|
|
1493
|
+
try {
|
|
1494
|
+
const s = await stat(filePath);
|
|
1495
|
+
snapshot.set(filePath, { mtimeMs: s.mtimeMs, size: s.size });
|
|
1496
|
+
} catch {
|
|
1497
|
+
}
|
|
1498
|
+
}
|
|
1499
|
+
log.debug(`Workspace snapshot: ${snapshot.size} files`);
|
|
1500
|
+
} catch (err) {
|
|
1501
|
+
log.debug(`Workspace snapshot failed: ${err}`);
|
|
1502
|
+
}
|
|
1503
|
+
return snapshot;
|
|
1504
|
+
}
|
|
1505
|
+
async function diffAndUpload(params) {
|
|
1506
|
+
const { workspace, snapshot, uploadUrl, uploadToken } = params;
|
|
1507
|
+
const currentFiles = await collectRealFiles(workspace);
|
|
1508
|
+
const newOrModified = [];
|
|
1509
|
+
for (const filePath of currentFiles) {
|
|
1510
|
+
try {
|
|
1511
|
+
const s = await stat(filePath);
|
|
1512
|
+
const prev = snapshot.get(filePath);
|
|
1513
|
+
if (!prev || s.mtimeMs !== prev.mtimeMs || s.size !== prev.size) {
|
|
1514
|
+
newOrModified.push(filePath);
|
|
1515
|
+
}
|
|
1516
|
+
} catch {
|
|
1517
|
+
}
|
|
1518
|
+
}
|
|
1519
|
+
if (newOrModified.length === 0) return [];
|
|
1520
|
+
log.debug(`Workspace diff: ${newOrModified.length} new/modified file(s)`);
|
|
1521
|
+
const attachments = [];
|
|
1522
|
+
const filesToUpload = newOrModified.slice(0, MAX_AUTO_UPLOAD_FILES);
|
|
1523
|
+
for (const absPath of filesToUpload) {
|
|
1524
|
+
try {
|
|
1525
|
+
const buffer = await readFile(absPath);
|
|
1526
|
+
if (buffer.length === 0 || buffer.length > MAX_AUTO_UPLOAD_FILE_SIZE) continue;
|
|
1527
|
+
const relPath = relative2(workspace, absPath).replace(/\\/g, "/");
|
|
1528
|
+
const filename = relPath && !relPath.startsWith("..") ? relPath : absPath.split("/").pop() || "file";
|
|
1529
|
+
const response = await fetch(uploadUrl, {
|
|
1530
|
+
method: "POST",
|
|
1531
|
+
headers: {
|
|
1532
|
+
"X-Upload-Token": uploadToken,
|
|
1533
|
+
"Content-Type": "application/json"
|
|
1534
|
+
},
|
|
1535
|
+
body: JSON.stringify({
|
|
1536
|
+
filename,
|
|
1537
|
+
content: buffer.toString("base64")
|
|
1538
|
+
})
|
|
1539
|
+
});
|
|
1540
|
+
if (!response.ok) {
|
|
1541
|
+
log.warn(`Auto-upload failed (${response.status}) for ${filename}`);
|
|
1542
|
+
continue;
|
|
1543
|
+
}
|
|
1544
|
+
const payload = await response.json();
|
|
1545
|
+
if (typeof payload.url === "string" && payload.url.length > 0) {
|
|
1546
|
+
const ext = filename.split(".").pop()?.toLowerCase() || "";
|
|
1547
|
+
attachments.push({
|
|
1548
|
+
name: filename,
|
|
1549
|
+
url: payload.url,
|
|
1550
|
+
type: MIME_MAP[ext] || "application/octet-stream"
|
|
1551
|
+
});
|
|
1552
|
+
}
|
|
1553
|
+
} catch (err) {
|
|
1554
|
+
log.warn(`Auto-upload error for ${absPath}: ${err}`);
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
return attachments;
|
|
1558
|
+
}
|
|
1559
|
+
|
|
1560
|
+
// src/adapters/claude.ts
|
|
1752
1561
|
var DEFAULT_IDLE_TIMEOUT = 30 * 60 * 1e3;
|
|
1753
1562
|
var MIN_IDLE_TIMEOUT = 60 * 1e3;
|
|
1754
1563
|
var HOME_DIR = homedir3();
|
|
@@ -2243,25 +2052,26 @@ var ClaudeAdapter = class extends AgentAdapter {
|
|
|
2243
2052
|
|
|
2244
2053
|
// src/commands/connect.ts
|
|
2245
2054
|
var DEFAULT_BRIDGE_URL = "wss://bridge.agents.hot/ws";
|
|
2055
|
+
function getWorkspaceHint() {
|
|
2056
|
+
return "Put CLAUDE.md (role instructions) and .claude/skills/ here.";
|
|
2057
|
+
}
|
|
2246
2058
|
function logWorkspaceHint(slug, projectPath) {
|
|
2247
2059
|
console.log(` ${GRAY}Workspace: ${RESET}${projectPath}`);
|
|
2248
|
-
console.log(` ${GRAY}
|
|
2060
|
+
console.log(` ${GRAY}${getWorkspaceHint()}${RESET}`);
|
|
2249
2061
|
}
|
|
2250
2062
|
function sleep2(ms) {
|
|
2251
2063
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
2252
2064
|
}
|
|
2253
2065
|
function createAdapter(type, config) {
|
|
2254
2066
|
switch (type) {
|
|
2255
|
-
case "openclaw":
|
|
2256
|
-
return new OpenClawAdapter(config);
|
|
2257
2067
|
case "claude":
|
|
2258
2068
|
return new ClaudeAdapter(config);
|
|
2259
2069
|
default:
|
|
2260
|
-
throw new Error(`Unknown agent type: ${type}. Supported:
|
|
2070
|
+
throw new Error(`Unknown agent type: ${type}. Supported: claude`);
|
|
2261
2071
|
}
|
|
2262
2072
|
}
|
|
2263
2073
|
function registerConnectCommand(program2) {
|
|
2264
|
-
program2.command("connect [type]").description("Connect a local agent to the Agents.Hot platform").option("--setup <url>", "One-click setup from agents.hot connect ticket URL").option("--agent-id <id>", "Agent ID registered on Agents.Hot").option("--project <path>", "
|
|
2074
|
+
program2.command("connect [type]").description("Connect a local agent to the Agents.Hot platform").option("--setup <url>", "One-click setup from agents.hot connect ticket URL").option("--agent-id <id>", "Agent ID registered on Agents.Hot").option("--project <path>", "Agent workspace path").option("--bridge-url <url>", "Bridge Worker WebSocket URL").option("--sandbox", "Run agent inside a sandbox (requires srt)").option("--no-sandbox", "Disable sandbox even if enabled in config").option("--foreground", "Run in foreground (default for non-setup mode)").action(async (type, opts) => {
|
|
2265
2075
|
const config = loadConfig();
|
|
2266
2076
|
if (opts.setup) {
|
|
2267
2077
|
log.info("Fetching configuration from connect ticket...");
|
|
@@ -2276,15 +2086,9 @@ function registerConnectCommand(program2) {
|
|
|
2276
2086
|
process.exit(1);
|
|
2277
2087
|
}
|
|
2278
2088
|
const ticketData = await response.json();
|
|
2279
|
-
|
|
2280
|
-
|
|
2281
|
-
|
|
2282
|
-
if (localToken) {
|
|
2283
|
-
gatewayToken = localToken;
|
|
2284
|
-
log.success("Auto-detected OpenClaw gateway token from ~/.openclaw/openclaw.json");
|
|
2285
|
-
} else {
|
|
2286
|
-
log.warn("Could not auto-detect OpenClaw token. Use --gateway-token to provide it manually.");
|
|
2287
|
-
}
|
|
2089
|
+
if (ticketData.agent_type !== "claude") {
|
|
2090
|
+
log.error(`Unsupported agent type from ticket: ${ticketData.agent_type}. Only claude is supported.`);
|
|
2091
|
+
process.exit(1);
|
|
2288
2092
|
}
|
|
2289
2093
|
let nameBase = ticketData.agent_id.slice(0, 8);
|
|
2290
2094
|
if (config.token) {
|
|
@@ -2308,8 +2112,6 @@ function registerConnectCommand(program2) {
|
|
|
2308
2112
|
agentId: ticketData.agent_id,
|
|
2309
2113
|
agentType: ticketData.agent_type,
|
|
2310
2114
|
bridgeUrl: ticketData.bridge_url,
|
|
2311
|
-
gatewayUrl: opts.gatewayUrl,
|
|
2312
|
-
gatewayToken,
|
|
2313
2115
|
projectPath: opts.project || getAgentWorkspaceDir(slug),
|
|
2314
2116
|
sandbox: opts.sandbox,
|
|
2315
2117
|
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -2320,7 +2122,6 @@ function registerConnectCommand(program2) {
|
|
|
2320
2122
|
if (opts.foreground) {
|
|
2321
2123
|
opts.agentId = ticketData.agent_id;
|
|
2322
2124
|
opts.bridgeUrl = ticketData.bridge_url;
|
|
2323
|
-
opts.gatewayToken = gatewayToken;
|
|
2324
2125
|
type = ticketData.agent_type;
|
|
2325
2126
|
} else {
|
|
2326
2127
|
const pid = spawnBackground(slug, entry, config.token);
|
|
@@ -2367,8 +2168,6 @@ function registerConnectCommand(program2) {
|
|
|
2367
2168
|
agentName = found.name;
|
|
2368
2169
|
const entry = found.entry;
|
|
2369
2170
|
opts.bridgeUrl = opts.bridgeUrl || entry.bridgeUrl;
|
|
2370
|
-
opts.gatewayUrl = opts.gatewayUrl || entry.gatewayUrl;
|
|
2371
|
-
opts.gatewayToken = opts.gatewayToken || entry.gatewayToken;
|
|
2372
2171
|
opts.project = opts.project || entry.projectPath || getAgentWorkspaceDir(found.name);
|
|
2373
2172
|
if (opts.sandbox === void 0 && entry.sandbox !== void 0) opts.sandbox = entry.sandbox;
|
|
2374
2173
|
}
|
|
@@ -2404,18 +2203,8 @@ function registerConnectCommand(program2) {
|
|
|
2404
2203
|
log.warn("Sandbox not available on this platform, continuing without sandbox");
|
|
2405
2204
|
}
|
|
2406
2205
|
}
|
|
2407
|
-
if (agentType === "openclaw") {
|
|
2408
|
-
const { isChatCompletionsEnabled } = await import("./openclaw-config-OFFNWVDK.js");
|
|
2409
|
-
if (!isChatCompletionsEnabled()) {
|
|
2410
|
-
log.warn(
|
|
2411
|
-
'OpenClaw chatCompletions endpoint may not be enabled.\n Add to ~/.openclaw/openclaw.json:\n { "gateway": { "http": { "endpoints": { "chatCompletions": { "enabled": true } } } } }\n Continuing anyway (gateway may be on a remote host)...'
|
|
2412
|
-
);
|
|
2413
|
-
}
|
|
2414
|
-
}
|
|
2415
2206
|
const adapterConfig = {
|
|
2416
2207
|
project: opts.project,
|
|
2417
|
-
gatewayUrl: opts.gatewayUrl,
|
|
2418
|
-
gatewayToken: opts.gatewayToken,
|
|
2419
2208
|
sandboxEnabled,
|
|
2420
2209
|
agentId
|
|
2421
2210
|
};
|
|
@@ -2447,8 +2236,6 @@ function registerConnectCommand(program2) {
|
|
|
2447
2236
|
agentId,
|
|
2448
2237
|
agentType,
|
|
2449
2238
|
bridgeUrl,
|
|
2450
|
-
gatewayUrl: opts.gatewayUrl,
|
|
2451
|
-
gatewayToken: opts.gatewayToken,
|
|
2452
2239
|
projectPath: opts.project,
|
|
2453
2240
|
sandbox: opts.sandbox,
|
|
2454
2241
|
addedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -2665,7 +2452,7 @@ function registerStatusCommand(program2) {
|
|
|
2665
2452
|
}
|
|
2666
2453
|
console.log("\nTo connect an agent, run:");
|
|
2667
2454
|
console.log(" agent-mesh connect <type> --agent-id <id>");
|
|
2668
|
-
console.log("\nSupported types:
|
|
2455
|
+
console.log("\nSupported types: claude");
|
|
2669
2456
|
});
|
|
2670
2457
|
}
|
|
2671
2458
|
|
|
@@ -3064,7 +2851,6 @@ var ERROR_HINTS = {
|
|
|
3064
2851
|
forbidden: "You don't own this agent.",
|
|
3065
2852
|
not_found: "Agent not found.",
|
|
3066
2853
|
agent_offline: "Agent must be online for first publish. Run `agent-mesh connect` first.",
|
|
3067
|
-
email_required: "Email required. Visit https://agents.hot/settings to add one.",
|
|
3068
2854
|
github_required: "GitHub account required. Visit https://agents.hot/settings to link one.",
|
|
3069
2855
|
validation_error: "Invalid input. Check your SKILL.md frontmatter or command flags.",
|
|
3070
2856
|
permission_denied: "You don't have permission to modify this skill.",
|
|
@@ -3198,6 +2984,22 @@ async function resolveAgentId(input, client) {
|
|
|
3198
2984
|
}
|
|
3199
2985
|
|
|
3200
2986
|
// src/commands/agents.ts
|
|
2987
|
+
var SUPPORTED_AGENT_TYPES = ["claude"];
|
|
2988
|
+
function normalizeAgentType(input) {
|
|
2989
|
+
if (!input) return null;
|
|
2990
|
+
const normalized = input.trim().toLowerCase();
|
|
2991
|
+
if (normalized === "claude-code" || normalized === "claudecode") return "claude";
|
|
2992
|
+
if (SUPPORTED_AGENT_TYPES.includes(normalized)) {
|
|
2993
|
+
return normalized;
|
|
2994
|
+
}
|
|
2995
|
+
return null;
|
|
2996
|
+
}
|
|
2997
|
+
function parseAgentTypeOrExit(input) {
|
|
2998
|
+
const agentType = normalizeAgentType(input);
|
|
2999
|
+
if (agentType) return agentType;
|
|
3000
|
+
log.error(`Invalid agent type: ${input}. Supported: ${SUPPORTED_AGENT_TYPES.join(", ")} (alias: claude-code).`);
|
|
3001
|
+
process.exit(1);
|
|
3002
|
+
}
|
|
3201
3003
|
function readLine(prompt) {
|
|
3202
3004
|
const rl = createInterface3({ input: process.stdin, output: process.stderr });
|
|
3203
3005
|
return new Promise((resolve2) => {
|
|
@@ -3256,10 +3058,10 @@ function registerAgentsCommand(program2) {
|
|
|
3256
3058
|
handleError(err);
|
|
3257
3059
|
}
|
|
3258
3060
|
});
|
|
3259
|
-
agents.command("create").description("Create a new agent").option("--name <name>", "Agent name").option("--type <type>", "Agent type (
|
|
3061
|
+
agents.command("create").description("Create a new agent").option("--name <name>", "Agent name").option("--type <type>", "Agent type (claude)", "claude").option("--description <desc>", "Agent description").action(async (opts) => {
|
|
3260
3062
|
try {
|
|
3261
3063
|
let { name, description } = opts;
|
|
3262
|
-
const agentType = opts.type;
|
|
3064
|
+
const agentType = parseAgentTypeOrExit(opts.type);
|
|
3263
3065
|
if (!name && process.stdin.isTTY) {
|
|
3264
3066
|
log.banner("Create Agent");
|
|
3265
3067
|
name = await readLine("Agent name: ");
|
|
@@ -3321,11 +3123,11 @@ function registerAgentsCommand(program2) {
|
|
|
3321
3123
|
handleError(err);
|
|
3322
3124
|
}
|
|
3323
3125
|
});
|
|
3324
|
-
agents.command("update <id-or-name>").description("Update an agent").option("--name <name>", "New name").option("--type <type>", "Agent type (
|
|
3126
|
+
agents.command("update <id-or-name>").description("Update an agent").option("--name <name>", "New name").option("--type <type>", "Agent type (claude)").option("--description <desc>", "Agent description").action(async (input, opts) => {
|
|
3325
3127
|
try {
|
|
3326
3128
|
const updates = {};
|
|
3327
3129
|
if (opts.name !== void 0) updates.name = opts.name;
|
|
3328
|
-
if (opts.type !== void 0) updates.agent_type = opts.type;
|
|
3130
|
+
if (opts.type !== void 0) updates.agent_type = parseAgentTypeOrExit(opts.type);
|
|
3329
3131
|
if (opts.description !== void 0) updates.description = opts.description;
|
|
3330
3132
|
if (Object.keys(updates).length === 0) {
|
|
3331
3133
|
log.error("No fields to update. Use --name, --type, --description.");
|
|
@@ -5348,6 +5150,56 @@ function registerRuntimeCommand(program2) {
|
|
|
5348
5150
|
});
|
|
5349
5151
|
}
|
|
5350
5152
|
|
|
5153
|
+
// src/commands/profile.ts
|
|
5154
|
+
import { spawn as spawn4 } from "child_process";
|
|
5155
|
+
var PROFILE_SETTINGS_URL = "https://agents.hot/settings?tab=profile";
|
|
5156
|
+
function handleError6(err) {
|
|
5157
|
+
if (err instanceof PlatformApiError) {
|
|
5158
|
+
log.error(err.message);
|
|
5159
|
+
} else {
|
|
5160
|
+
log.error(err.message);
|
|
5161
|
+
}
|
|
5162
|
+
process.exit(1);
|
|
5163
|
+
}
|
|
5164
|
+
function openUrl(url) {
|
|
5165
|
+
console.log(` Opening ${GRAY}${url}${RESET}...`);
|
|
5166
|
+
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
|
|
5167
|
+
const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
|
|
5168
|
+
const child = spawn4(cmd, args, { detached: true, stdio: "ignore" });
|
|
5169
|
+
child.unref();
|
|
5170
|
+
}
|
|
5171
|
+
function registerProfileCommand(program2) {
|
|
5172
|
+
const profile = program2.command("profile").description("Manage your Agents.Hot profile settings");
|
|
5173
|
+
profile.command("open").description("Open profile settings page in browser").action(() => {
|
|
5174
|
+
try {
|
|
5175
|
+
openUrl(PROFILE_SETTINGS_URL);
|
|
5176
|
+
} catch {
|
|
5177
|
+
console.log(` Open this URL: ${PROFILE_SETTINGS_URL}`);
|
|
5178
|
+
}
|
|
5179
|
+
});
|
|
5180
|
+
profile.command("copy-login-email").description("Copy your login email into the public contact email field").action(async () => {
|
|
5181
|
+
try {
|
|
5182
|
+
const client = createClient();
|
|
5183
|
+
const profileData = await client.get("/api/user/profile");
|
|
5184
|
+
const loginEmail = profileData.email?.trim();
|
|
5185
|
+
if (!loginEmail) {
|
|
5186
|
+
log.error("No login email found for this account. Set a public contact email manually.");
|
|
5187
|
+
console.log(` Run: ${GRAY}agent-mesh profile open${RESET}`);
|
|
5188
|
+
process.exit(1);
|
|
5189
|
+
}
|
|
5190
|
+
if ((profileData.author_email || "").trim() === loginEmail) {
|
|
5191
|
+
log.success(`Public contact email already matches login email: ${loginEmail}`);
|
|
5192
|
+
return;
|
|
5193
|
+
}
|
|
5194
|
+
await client.patch("/api/user/profile", { email: loginEmail });
|
|
5195
|
+
log.success(`Public contact email updated: ${loginEmail}`);
|
|
5196
|
+
log.warn("This email is public on your author profile and used for agent contact.");
|
|
5197
|
+
} catch (err) {
|
|
5198
|
+
handleError6(err);
|
|
5199
|
+
}
|
|
5200
|
+
});
|
|
5201
|
+
}
|
|
5202
|
+
|
|
5351
5203
|
// src/index.ts
|
|
5352
5204
|
var require2 = createRequire(import.meta.url);
|
|
5353
5205
|
var { version } = require2("../package.json");
|
|
@@ -5378,4 +5230,5 @@ registerSubscribeCommand(program);
|
|
|
5378
5230
|
registerRegisterCommand(program);
|
|
5379
5231
|
registerRateCommand(program);
|
|
5380
5232
|
registerRuntimeCommand(program);
|
|
5233
|
+
registerProfileCommand(program);
|
|
5381
5234
|
program.parse();
|
package/package.json
CHANGED
package/dist/chunk-W24WCWEC.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// src/utils/openclaw-config.ts
|
|
4
|
-
import { readFileSync } from "fs";
|
|
5
|
-
import { join } from "path";
|
|
6
|
-
import { homedir } from "os";
|
|
7
|
-
|
|
8
|
-
// src/utils/logger.ts
|
|
9
|
-
var RESET = "\x1B[0m";
|
|
10
|
-
var RED = "\x1B[31m";
|
|
11
|
-
var GREEN = "\x1B[32m";
|
|
12
|
-
var YELLOW = "\x1B[33m";
|
|
13
|
-
var BLUE = "\x1B[34m";
|
|
14
|
-
var GRAY = "\x1B[90m";
|
|
15
|
-
var BOLD = "\x1B[1m";
|
|
16
|
-
function timestamp() {
|
|
17
|
-
return (/* @__PURE__ */ new Date()).toISOString().slice(11, 19);
|
|
18
|
-
}
|
|
19
|
-
var log = {
|
|
20
|
-
info(msg, ...args) {
|
|
21
|
-
console.log(`${GRAY}${timestamp()}${RESET} ${BLUE}INFO${RESET} ${msg}`, ...args);
|
|
22
|
-
},
|
|
23
|
-
success(msg, ...args) {
|
|
24
|
-
console.log(`${GRAY}${timestamp()}${RESET} ${GREEN}OK${RESET} ${msg}`, ...args);
|
|
25
|
-
},
|
|
26
|
-
warn(msg, ...args) {
|
|
27
|
-
console.warn(`${GRAY}${timestamp()}${RESET} ${YELLOW}WARN${RESET} ${msg}`, ...args);
|
|
28
|
-
},
|
|
29
|
-
error(msg, ...args) {
|
|
30
|
-
console.error(`${GRAY}${timestamp()}${RESET} ${RED}ERROR${RESET} ${msg}`, ...args);
|
|
31
|
-
},
|
|
32
|
-
debug(msg, ...args) {
|
|
33
|
-
if (process.env.DEBUG) {
|
|
34
|
-
console.log(`${GRAY}${timestamp()} DEBUG ${msg}${RESET}`, ...args);
|
|
35
|
-
}
|
|
36
|
-
},
|
|
37
|
-
banner(text) {
|
|
38
|
-
console.log(`
|
|
39
|
-
${BOLD}${text}${RESET}
|
|
40
|
-
`);
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
// src/utils/openclaw-config.ts
|
|
45
|
-
var OPENCLAW_CONFIG_PATH = join(homedir(), ".openclaw", "openclaw.json");
|
|
46
|
-
function readOpenClawConfig(configPath) {
|
|
47
|
-
const path = configPath || OPENCLAW_CONFIG_PATH;
|
|
48
|
-
try {
|
|
49
|
-
const raw = readFileSync(path, "utf-8");
|
|
50
|
-
return JSON.parse(raw);
|
|
51
|
-
} catch {
|
|
52
|
-
return null;
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
function isChatCompletionsEnabled(configPath) {
|
|
56
|
-
const config = readOpenClawConfig(configPath);
|
|
57
|
-
if (!config) return false;
|
|
58
|
-
try {
|
|
59
|
-
const enabled = config?.gateway?.http?.endpoints?.chatCompletions?.enabled;
|
|
60
|
-
return enabled === true;
|
|
61
|
-
} catch {
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
function readOpenClawToken(configPath) {
|
|
66
|
-
const path = configPath || OPENCLAW_CONFIG_PATH;
|
|
67
|
-
try {
|
|
68
|
-
const raw = readFileSync(path, "utf-8");
|
|
69
|
-
const config = JSON.parse(raw);
|
|
70
|
-
const token = config?.gateway?.auth?.token;
|
|
71
|
-
if (typeof token === "string" && token.length > 0) {
|
|
72
|
-
return token;
|
|
73
|
-
}
|
|
74
|
-
log.warn("OpenClaw config found but gateway.auth.token is missing or empty");
|
|
75
|
-
return null;
|
|
76
|
-
} catch {
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export {
|
|
82
|
-
log,
|
|
83
|
-
readOpenClawConfig,
|
|
84
|
-
isChatCompletionsEnabled,
|
|
85
|
-
readOpenClawToken
|
|
86
|
-
};
|