@xalia/agent 1.0.19
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/.prettierrc.json +11 -0
- package/README.md +57 -0
- package/dist/agent.js +278 -0
- package/dist/agentUtils.js +88 -0
- package/dist/chat.js +278 -0
- package/dist/dummyLLM.js +28 -0
- package/dist/files.js +115 -0
- package/dist/iplatform.js +2 -0
- package/dist/llm.js +2 -0
- package/dist/main.js +136 -0
- package/dist/mcpServerManager.js +269 -0
- package/dist/nodePlatform.js +61 -0
- package/dist/openAILLM.js +31 -0
- package/dist/options.js +79 -0
- package/dist/prompt.js +83 -0
- package/dist/sudoMcpServerManager.js +174 -0
- package/dist/test/imageLoad.test.js +14 -0
- package/dist/test/mcpServerManager.test.js +71 -0
- package/dist/test/prompt.test.js +26 -0
- package/dist/test/sudoMcpServerManager.test.js +49 -0
- package/dist/tokenAuth.js +39 -0
- package/dist/tools.js +44 -0
- package/eslint.config.mjs +25 -0
- package/frog.png +0 -0
- package/package.json +41 -0
- package/scripts/git_message +31 -0
- package/scripts/git_wip +21 -0
- package/scripts/pr_message +18 -0
- package/scripts/pr_review +16 -0
- package/scripts/sudomcp_import +23 -0
- package/scripts/test_script +60 -0
- package/src/agent.ts +357 -0
- package/src/agentUtils.ts +188 -0
- package/src/chat.ts +325 -0
- package/src/dummyLLM.ts +36 -0
- package/src/files.ts +95 -0
- package/src/iplatform.ts +11 -0
- package/src/llm.ts +12 -0
- package/src/main.ts +171 -0
- package/src/mcpServerManager.ts +365 -0
- package/src/nodePlatform.ts +24 -0
- package/src/openAILLM.ts +43 -0
- package/src/options.ts +103 -0
- package/src/prompt.ts +93 -0
- package/src/sudoMcpServerManager.ts +268 -0
- package/src/test/imageLoad.test.ts +14 -0
- package/src/test/mcpServerManager.test.ts +98 -0
- package/src/test/prompt.test.src +0 -0
- package/src/test/prompt.test.ts +26 -0
- package/src/test/sudoMcpServerManager.test.ts +63 -0
- package/src/tokenAuth.ts +50 -0
- package/src/tools.ts +57 -0
- package/test_data/background_test_profile.json +7 -0
- package/test_data/background_test_script.json +11 -0
- package/test_data/dummyllm_script_simplecalc.json +28 -0
- package/test_data/git_message_profile.json +4 -0
- package/test_data/git_wip_system.txt +5 -0
- package/test_data/pr_message_profile.json +4 -0
- package/test_data/pr_review_profile.json +4 -0
- package/test_data/prompt_simplecalc.txt +1 -0
- package/test_data/simplecalc_profile.json +4 -0
- package/test_data/sudomcp_import_profile.json +4 -0
- package/test_data/test_script_profile.json +9 -0
- package/tsconfig.json +13 -0
package/dist/chat.js
ADDED
@@ -0,0 +1,278 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
+
if (k2 === undefined) k2 = k;
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
7
|
+
}
|
8
|
+
Object.defineProperty(o, k2, desc);
|
9
|
+
}) : (function(o, m, k, k2) {
|
10
|
+
if (k2 === undefined) k2 = k;
|
11
|
+
o[k2] = m[k];
|
12
|
+
}));
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
15
|
+
}) : function(o, v) {
|
16
|
+
o["default"] = v;
|
17
|
+
});
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
19
|
+
var ownKeys = function(o) {
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
21
|
+
var ar = [];
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
23
|
+
return ar;
|
24
|
+
};
|
25
|
+
return ownKeys(o);
|
26
|
+
};
|
27
|
+
return function (mod) {
|
28
|
+
if (mod && mod.__esModule) return mod;
|
29
|
+
var result = {};
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
31
|
+
__setModuleDefault(result, mod);
|
32
|
+
return result;
|
33
|
+
};
|
34
|
+
})();
|
35
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
37
|
+
};
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
39
|
+
exports.runChat = runChat;
|
40
|
+
const yocto_spinner_1 = __importDefault(require("yocto-spinner"));
|
41
|
+
const fs = __importStar(require("fs"));
|
42
|
+
const tools_1 = require("./tools");
|
43
|
+
const files_1 = require("./files");
|
44
|
+
const prompt_1 = require("./prompt");
|
45
|
+
const chalk_1 = __importDefault(require("chalk"));
|
46
|
+
const tool_1 = require("@xalia/xmcp/tool");
|
47
|
+
const sdk_1 = require("@xalia/xmcp/sdk");
|
48
|
+
const agentUtils_1 = require("./agentUtils");
|
49
|
+
const nodePlatform_1 = require("./nodePlatform");
|
50
|
+
const logger = (0, sdk_1.getLogger)();
|
51
|
+
async function runChat(agentProfile, conversation, prompt, image, openaiApiKey, sudomcpConfig, approveToolsUpTo) {
|
52
|
+
// In chat mode, just print the messages. Ask for tool confirmation
|
53
|
+
// unless approveTools is set.
|
54
|
+
const spinner = (0, yocto_spinner_1.default)();
|
55
|
+
const onMessage = async (msg, _msgEnd) => {
|
56
|
+
if (msg.content) {
|
57
|
+
if (spinner) {
|
58
|
+
spinner.stop().clear();
|
59
|
+
}
|
60
|
+
console.log(`AGENT: ${msg.content}`);
|
61
|
+
}
|
62
|
+
};
|
63
|
+
let remainingApprovedToolCalls = approveToolsUpTo;
|
64
|
+
const onToolCall = async (toolCall) => {
|
65
|
+
if (remainingApprovedToolCalls !== 0) {
|
66
|
+
--remainingApprovedToolCalls;
|
67
|
+
return true;
|
68
|
+
}
|
69
|
+
spinner.stop().clear();
|
70
|
+
const result = await promptToolCall(repl, toolCall);
|
71
|
+
spinner.start();
|
72
|
+
return result;
|
73
|
+
};
|
74
|
+
// Create agent
|
75
|
+
const [agent, sudoMcpServerManager] = await (0, agentUtils_1.createAgentAndSudoMcpServerManager)(agentProfile, onMessage, onToolCall, nodePlatform_1.NODE_PLATFORM, openaiApiKey, sudomcpConfig, tool_1.authenticate.FRONTEND_PROD_AUTHORIZED_URL, undefined);
|
76
|
+
if (conversation) {
|
77
|
+
agent.setConversation(conversation);
|
78
|
+
}
|
79
|
+
// Opening banner
|
80
|
+
console.log(`${chalk_1.default.green("SudoMCP Agent CLI")}`);
|
81
|
+
console.log(`(Type ${chalk_1.default.yellow("/h")} for help, ${chalk_1.default.yellow("/q")} to quit.)`);
|
82
|
+
// Display first prompt if supplied
|
83
|
+
if (prompt) {
|
84
|
+
console.log(`USER: ${prompt}`);
|
85
|
+
}
|
86
|
+
const repl = new prompt_1.Prompt();
|
87
|
+
// Conversation loop
|
88
|
+
while (true) {
|
89
|
+
if (!prompt) {
|
90
|
+
const [msg, img] = await getNextPrompt(repl, agent, sudoMcpServerManager);
|
91
|
+
if (!msg && !img) {
|
92
|
+
break;
|
93
|
+
}
|
94
|
+
prompt = msg;
|
95
|
+
image = img;
|
96
|
+
}
|
97
|
+
image = (0, files_1.loadImageB64OrUndefined)(image);
|
98
|
+
// Pass the prompt and image to the Agent
|
99
|
+
try {
|
100
|
+
spinner.start();
|
101
|
+
await agent.userMessage(prompt, image);
|
102
|
+
}
|
103
|
+
catch (e) {
|
104
|
+
console.log(`ERROR: ${e}`);
|
105
|
+
}
|
106
|
+
prompt = undefined;
|
107
|
+
image = undefined;
|
108
|
+
}
|
109
|
+
// Shutdown the agent
|
110
|
+
repl.shutdown();
|
111
|
+
await agent.shutdown();
|
112
|
+
logger.debug("shutdown done");
|
113
|
+
}
|
114
|
+
/**
|
115
|
+
* Read lines from the prompt, parsing any commands, and return once there is
|
116
|
+
* a prompt and/or image for the llm. Both undefined means exit.
|
117
|
+
*/
|
118
|
+
async function getNextPrompt(repl, agent, sudoMcpServerManager) {
|
119
|
+
while (true) {
|
120
|
+
// Get a line, detecting the EOF signal.
|
121
|
+
const line = await repl.run();
|
122
|
+
if (typeof line === "undefined") {
|
123
|
+
console.log("closing ...");
|
124
|
+
return [undefined, undefined];
|
125
|
+
}
|
126
|
+
if (line.length === 0) {
|
127
|
+
continue;
|
128
|
+
}
|
129
|
+
// Extract prompt or commands
|
130
|
+
const { msg, cmds } = (0, prompt_1.parsePrompt)(line);
|
131
|
+
// If there are no commands, this must be a prompt only
|
132
|
+
if (!cmds) {
|
133
|
+
return [msg, undefined];
|
134
|
+
}
|
135
|
+
// There are commands. If it's image, return [prompt, image]. If it's
|
136
|
+
// quit, return [undefined, undefined], otherwise it must be a command.
|
137
|
+
// Execute it and prompt again.
|
138
|
+
switch (cmds[0]) {
|
139
|
+
case "i": // image
|
140
|
+
return [msg, cmds[1]];
|
141
|
+
case "q":
|
142
|
+
case "quit":
|
143
|
+
case "exit":
|
144
|
+
return [undefined, undefined];
|
145
|
+
default:
|
146
|
+
break;
|
147
|
+
}
|
148
|
+
try {
|
149
|
+
await runCommand(agent, agent.getMcpServerManager(), sudoMcpServerManager, cmds);
|
150
|
+
}
|
151
|
+
catch (e) {
|
152
|
+
console.log(`ERROR: ${e}`);
|
153
|
+
}
|
154
|
+
}
|
155
|
+
}
|
156
|
+
async function runCommand(agent, mcpServerManager, sudoMcpServerManager, cmds) {
|
157
|
+
switch (cmds[0]) {
|
158
|
+
case "lt":
|
159
|
+
listTools(mcpServerManager);
|
160
|
+
break;
|
161
|
+
case "ls":
|
162
|
+
listServers(sudoMcpServerManager);
|
163
|
+
break;
|
164
|
+
case "as":
|
165
|
+
await addServer(sudoMcpServerManager, cmds[1]);
|
166
|
+
break;
|
167
|
+
case "rs":
|
168
|
+
await mcpServerManager.removeMcpServer(cmds[1]);
|
169
|
+
break;
|
170
|
+
case "e":
|
171
|
+
mcpServerManager.enableTool(cmds[1], cmds[2]);
|
172
|
+
console.log(`Enabled tool ${cmds[2]} for server ${cmds[1]}`);
|
173
|
+
break;
|
174
|
+
case "d":
|
175
|
+
mcpServerManager.disableTool(cmds[1], cmds[2]);
|
176
|
+
console.log(`Disabled tool ${cmds[2]} for server ${cmds[1]}`);
|
177
|
+
break;
|
178
|
+
case "ea":
|
179
|
+
mcpServerManager.enableAllTools(cmds[1]);
|
180
|
+
console.log(`Enabled all tools for server ${cmds[1]}`);
|
181
|
+
break;
|
182
|
+
case "da":
|
183
|
+
mcpServerManager.disableAllTools(cmds[1]);
|
184
|
+
console.log(`Disabled all tools for server ${cmds[1]}`);
|
185
|
+
break;
|
186
|
+
case "wc":
|
187
|
+
writeConversation(agent, cmds[1]);
|
188
|
+
break;
|
189
|
+
case "wa":
|
190
|
+
writeAgentProfile(agent, cmds[1]);
|
191
|
+
break;
|
192
|
+
case "h":
|
193
|
+
case "help":
|
194
|
+
case "?":
|
195
|
+
helpMenu();
|
196
|
+
break;
|
197
|
+
default:
|
198
|
+
console.log(`error: Unknown command ${cmds[0]}`);
|
199
|
+
}
|
200
|
+
}
|
201
|
+
function helpMenu() {
|
202
|
+
console.log(`Tool management commands:`);
|
203
|
+
console.log(` ${chalk_1.default.yellow("/lt")} List tools: `);
|
204
|
+
console.log(` ${chalk_1.default.yellow("/ls")} List servers`);
|
205
|
+
console.log(` ${chalk_1.default.yellow("/as <server-name>")} Add server`);
|
206
|
+
console.log(` ${chalk_1.default.yellow("/rs <server-name>")} Remove server`);
|
207
|
+
console.log(` ${chalk_1.default.yellow("/e <server-name> <tool-name>")} Enable tool`);
|
208
|
+
console.log(` ${chalk_1.default.yellow("/d <server-name> <tool-name>")} Disable tool`);
|
209
|
+
console.log(` ${chalk_1.default.yellow("/ea <server-name>")} Enable all tools`);
|
210
|
+
console.log(` ${chalk_1.default.yellow("/da <server-name>")} Disable all tools`);
|
211
|
+
console.log(` ${chalk_1.default.yellow("/wc <file-name>")} Write conversation file`);
|
212
|
+
console.log(` ${chalk_1.default.yellow("/wa <file-name>")} Write agent profile file`);
|
213
|
+
console.log(` ${chalk_1.default.yellow("/q")} Quit`);
|
214
|
+
}
|
215
|
+
function listTools(mcpServerManager) {
|
216
|
+
console.log("Mcp servers and tools (* - enabled):");
|
217
|
+
const serverNames = mcpServerManager.getMcpServerNames();
|
218
|
+
for (const serverName of serverNames) {
|
219
|
+
const server = mcpServerManager.getMcpServer(serverName);
|
220
|
+
console.log(` ${chalk_1.default.green(serverName)}`);
|
221
|
+
const tools = server.getTools();
|
222
|
+
const enabled = server.getEnabledTools();
|
223
|
+
for (const tool of tools) {
|
224
|
+
const isEnabled = enabled[tool.name] ? "*" : " ";
|
225
|
+
console.log(` [${isEnabled}] ${tool.name}`);
|
226
|
+
}
|
227
|
+
}
|
228
|
+
}
|
229
|
+
function listServers(sudoMcpServerManager) {
|
230
|
+
console.log(`Available MCP Servers:`);
|
231
|
+
const serverBriefs = sudoMcpServerManager.getServerBriefs();
|
232
|
+
serverBriefs.sort((a, b) => a.name.localeCompare(b.name));
|
233
|
+
for (const brief of serverBriefs) {
|
234
|
+
console.log(`- ${chalk_1.default.green(brief.name)}: ${brief.description}`);
|
235
|
+
}
|
236
|
+
}
|
237
|
+
/**
|
238
|
+
* Adds server and enables all tools.
|
239
|
+
*/
|
240
|
+
async function addServer(sudoMcpServerManager, serverName) {
|
241
|
+
const sudoMcpConfig = tool_1.configuration.loadEnsureConfig();
|
242
|
+
const config = sudoMcpConfig
|
243
|
+
? sudoMcpConfig.getConfigForServer(serverName)
|
244
|
+
: {};
|
245
|
+
try {
|
246
|
+
await sudoMcpServerManager.addMcpServer(serverName, config);
|
247
|
+
}
|
248
|
+
catch (e) {
|
249
|
+
if (e instanceof sdk_1.InvalidConfiguration) {
|
250
|
+
throw `${e}. \n
|
251
|
+
Have you installed server ${serverName} using the SudoMCP CLI tool?`;
|
252
|
+
}
|
253
|
+
else {
|
254
|
+
throw e;
|
255
|
+
}
|
256
|
+
}
|
257
|
+
sudoMcpServerManager.getMcpServerManager().enableAllTools(serverName);
|
258
|
+
console.log(`Added server: ${serverName} and enabled all tools.`);
|
259
|
+
console.log(`Current tool list:`);
|
260
|
+
listTools(sudoMcpServerManager.getMcpServerManager());
|
261
|
+
}
|
262
|
+
function writeConversation(agent, fileName) {
|
263
|
+
const conversation = agent.getConversation();
|
264
|
+
fs.writeFileSync(fileName, JSON.stringify(conversation), {
|
265
|
+
encoding: "utf8",
|
266
|
+
});
|
267
|
+
console.log(`Conversation written to ${fileName}`);
|
268
|
+
}
|
269
|
+
function writeAgentProfile(agent, fileName) {
|
270
|
+
const profile = agent.getAgentProfile();
|
271
|
+
fs.writeFileSync(fileName, JSON.stringify(profile));
|
272
|
+
console.log(`AgentProfile written to ${fileName}`);
|
273
|
+
}
|
274
|
+
async function promptToolCall(repl, toolCall) {
|
275
|
+
(0, tools_1.displayToolCall)(toolCall);
|
276
|
+
const response = await repl.run("Approve tool call? (Y/n) ");
|
277
|
+
return response === "y" || response === "yes" || response == "";
|
278
|
+
}
|
package/dist/dummyLLM.js
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
"use strict";
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
3
|
+
exports.DummyLLM = void 0;
|
4
|
+
const assert_1 = require("assert");
|
5
|
+
class DummyLLM {
|
6
|
+
constructor(responses) {
|
7
|
+
this.responses = responses;
|
8
|
+
this.idx = 0;
|
9
|
+
}
|
10
|
+
getModel() {
|
11
|
+
return "dummy";
|
12
|
+
}
|
13
|
+
getUrl() {
|
14
|
+
throw "cannot get url for DummyLLM";
|
15
|
+
}
|
16
|
+
async getConversationResponse(_messages, _tools) {
|
17
|
+
await new Promise((r) => setTimeout(r, 0));
|
18
|
+
(0, assert_1.strict)(this.idx < this.responses.length);
|
19
|
+
return {
|
20
|
+
id: "" + this.idx,
|
21
|
+
choices: [this.responses[this.idx++]],
|
22
|
+
created: Date.now(),
|
23
|
+
model: "dummyLlmModel",
|
24
|
+
object: "chat.completion",
|
25
|
+
};
|
26
|
+
}
|
27
|
+
}
|
28
|
+
exports.DummyLLM = DummyLLM;
|
package/dist/files.js
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
"use strict";
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
3
|
+
if (k2 === undefined) k2 = k;
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
7
|
+
}
|
8
|
+
Object.defineProperty(o, k2, desc);
|
9
|
+
}) : (function(o, m, k, k2) {
|
10
|
+
if (k2 === undefined) k2 = k;
|
11
|
+
o[k2] = m[k];
|
12
|
+
}));
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
15
|
+
}) : function(o, v) {
|
16
|
+
o["default"] = v;
|
17
|
+
});
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
19
|
+
var ownKeys = function(o) {
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
21
|
+
var ar = [];
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
23
|
+
return ar;
|
24
|
+
};
|
25
|
+
return ownKeys(o);
|
26
|
+
};
|
27
|
+
return function (mod) {
|
28
|
+
if (mod && mod.__esModule) return mod;
|
29
|
+
var result = {};
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
31
|
+
__setModuleDefault(result, mod);
|
32
|
+
return result;
|
33
|
+
};
|
34
|
+
})();
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
36
|
+
exports.loadFileOrUndefined = loadFileOrUndefined;
|
37
|
+
exports.loadFileOrStdin = loadFileOrStdin;
|
38
|
+
exports.loadImageB64OrUndefined = loadImageB64OrUndefined;
|
39
|
+
exports.loadServerUrls = loadServerUrls;
|
40
|
+
const fs = __importStar(require("fs"));
|
41
|
+
const path = __importStar(require("path"));
|
42
|
+
function loadFileOrUndefined(file) {
|
43
|
+
if (file) {
|
44
|
+
return fs.readFileSync(file, { encoding: "utf8" });
|
45
|
+
}
|
46
|
+
return undefined;
|
47
|
+
}
|
48
|
+
/**
|
49
|
+
* If filename is `-` read everything from stdin and return it. Otherwise,
|
50
|
+
* load the file and return the content.
|
51
|
+
*/
|
52
|
+
async function loadFileOrStdin(file) {
|
53
|
+
if (file === "-") {
|
54
|
+
return new Promise((resolve, reject) => {
|
55
|
+
let data = "";
|
56
|
+
process.stdin.setEncoding("utf8");
|
57
|
+
process.stdin.on("data", (chunk) => {
|
58
|
+
data += chunk;
|
59
|
+
});
|
60
|
+
process.stdin.on("end", () => {
|
61
|
+
resolve(data);
|
62
|
+
});
|
63
|
+
process.stdin.on("error", (err) => {
|
64
|
+
reject(err);
|
65
|
+
});
|
66
|
+
});
|
67
|
+
}
|
68
|
+
return fs.readFileSync(file, { encoding: "utf8" });
|
69
|
+
}
|
70
|
+
function loadImageB64OrUndefined(file) {
|
71
|
+
if (file) {
|
72
|
+
const ext = path.extname(file).slice(1).toLowerCase();
|
73
|
+
const mimeType = {
|
74
|
+
jpg: "image/jpeg",
|
75
|
+
jpeg: "image/jpeg",
|
76
|
+
png: "image/png",
|
77
|
+
gif: "image/gif",
|
78
|
+
svg: "image/svg+xml",
|
79
|
+
webp: "image/webp",
|
80
|
+
}[ext];
|
81
|
+
if (!mimeType) {
|
82
|
+
throw Error(`invalid file extension: ${ext}`);
|
83
|
+
}
|
84
|
+
const fileBuffer = fs.readFileSync(file);
|
85
|
+
const imgB64 = fileBuffer.toString("base64");
|
86
|
+
return `data:${mimeType};base64,${imgB64}`;
|
87
|
+
}
|
88
|
+
return undefined;
|
89
|
+
}
|
90
|
+
function loadServerUrls(path) {
|
91
|
+
try {
|
92
|
+
const file = fs.readFileSync(path, "utf-8");
|
93
|
+
const urls = JSON.parse(file);
|
94
|
+
// Validate the structure
|
95
|
+
if (typeof urls !== "object" || urls === null) {
|
96
|
+
throw new Error("Invalid server URLs format: must be an object");
|
97
|
+
}
|
98
|
+
// Validate each URL is a string
|
99
|
+
for (const [key, value] of Object.entries(urls)) {
|
100
|
+
if (typeof value !== "string") {
|
101
|
+
throw new Error(`Invalid URL format for server ${key}: must be a string`);
|
102
|
+
}
|
103
|
+
}
|
104
|
+
return urls;
|
105
|
+
}
|
106
|
+
catch (error) {
|
107
|
+
if (error instanceof SyntaxError) {
|
108
|
+
throw new Error(`Invalid JSON in server URLs file: ${error.message}`);
|
109
|
+
}
|
110
|
+
if (error instanceof Error) {
|
111
|
+
throw new Error(`Failed to load server URLs: ${error.message}`);
|
112
|
+
}
|
113
|
+
throw error;
|
114
|
+
}
|
115
|
+
}
|
package/dist/llm.js
ADDED
package/dist/main.js
ADDED
@@ -0,0 +1,136 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
"use strict";
|
3
|
+
// -*- typescript -*-
|
4
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
5
|
+
if (k2 === undefined) k2 = k;
|
6
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
7
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
8
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
9
|
+
}
|
10
|
+
Object.defineProperty(o, k2, desc);
|
11
|
+
}) : (function(o, m, k, k2) {
|
12
|
+
if (k2 === undefined) k2 = k;
|
13
|
+
o[k2] = m[k];
|
14
|
+
}));
|
15
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
16
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
17
|
+
}) : function(o, v) {
|
18
|
+
o["default"] = v;
|
19
|
+
});
|
20
|
+
var __importStar = (this && this.__importStar) || (function () {
|
21
|
+
var ownKeys = function(o) {
|
22
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
23
|
+
var ar = [];
|
24
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
25
|
+
return ar;
|
26
|
+
};
|
27
|
+
return ownKeys(o);
|
28
|
+
};
|
29
|
+
return function (mod) {
|
30
|
+
if (mod && mod.__esModule) return mod;
|
31
|
+
var result = {};
|
32
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
33
|
+
__setModuleDefault(result, mod);
|
34
|
+
return result;
|
35
|
+
};
|
36
|
+
})();
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
38
|
+
const fs = __importStar(require("fs"));
|
39
|
+
const dotenv = __importStar(require("dotenv"));
|
40
|
+
const cmd_ts_1 = require("cmd-ts");
|
41
|
+
const agent_1 = require("./agent");
|
42
|
+
const options_1 = require("./options");
|
43
|
+
const files_1 = require("./files");
|
44
|
+
const tool_1 = require("@xalia/xmcp/tool");
|
45
|
+
const sdk_1 = require("@xalia/xmcp/sdk");
|
46
|
+
const agentUtils_1 = require("./agentUtils");
|
47
|
+
const chat_1 = require("./chat");
|
48
|
+
const nodePlatform_1 = require("./nodePlatform");
|
49
|
+
const assert_1 = require("assert");
|
50
|
+
dotenv.config();
|
51
|
+
const logger = (0, sdk_1.getLogger)();
|
52
|
+
const main = (0, cmd_ts_1.command)({
|
53
|
+
name: "main",
|
54
|
+
args: {
|
55
|
+
promptFile: options_1.promptFile,
|
56
|
+
imageFile: options_1.imageFile,
|
57
|
+
openaiApiKey: options_1.openaiApiKey,
|
58
|
+
oneShot: options_1.oneShot,
|
59
|
+
approveTools: options_1.approveTools,
|
60
|
+
approveToolsUpTo: options_1.approveToolsUpTo,
|
61
|
+
sudomcpConfigFile: (0, cmd_ts_1.option)({
|
62
|
+
type: (0, cmd_ts_1.optional)(cmd_ts_1.string),
|
63
|
+
long: "sudomcp-config-file",
|
64
|
+
description: "SudoMCP config file (content or file, default: ~/config/..)",
|
65
|
+
env: "SUDOMCP_CONFIG",
|
66
|
+
}),
|
67
|
+
conversationFile: (0, cmd_ts_1.option)({
|
68
|
+
type: (0, cmd_ts_1.optional)(cmd_ts_1.string),
|
69
|
+
long: "conversation",
|
70
|
+
description: "Restore conversation (content or file) (see /wc)",
|
71
|
+
env: "CONVERSATION",
|
72
|
+
}),
|
73
|
+
conversationOutputFile: (0, cmd_ts_1.option)({
|
74
|
+
type: (0, cmd_ts_1.optional)(cmd_ts_1.string),
|
75
|
+
long: "conversation-output",
|
76
|
+
description: "Save final conversation to file (--one-shot mode)",
|
77
|
+
env: "CONVERSATION_OUTPUT",
|
78
|
+
}),
|
79
|
+
agentProfileFile: (0, cmd_ts_1.option)({
|
80
|
+
type: (0, cmd_ts_1.optional)(cmd_ts_1.string),
|
81
|
+
long: "agent-profile",
|
82
|
+
description: "Agent profile (content or filename)",
|
83
|
+
env: "AGENT_PROFILE",
|
84
|
+
}),
|
85
|
+
},
|
86
|
+
handler: async ({ promptFile, imageFile, openaiApiKey, oneShot, approveTools, approveToolsUpTo, sudomcpConfigFile, conversationFile, conversationOutputFile, agentProfileFile, }) => {
|
87
|
+
approveToolsUpTo = (() => {
|
88
|
+
if (typeof approveToolsUpTo === "undefined") {
|
89
|
+
// For the non-interactive case, `--approve-tools` is ignored.
|
90
|
+
if (oneShot) {
|
91
|
+
return -1;
|
92
|
+
}
|
93
|
+
return approveTools ? -1 : 0;
|
94
|
+
}
|
95
|
+
return approveToolsUpTo;
|
96
|
+
})();
|
97
|
+
(0, assert_1.strict)(typeof approveToolsUpTo === "number");
|
98
|
+
// Load the AgentProfile or use a default
|
99
|
+
const agentProfile = (() => {
|
100
|
+
let agentProfile = tool_1.utils.loadContentOrFileOrUndefined(agentProfileFile, agent_1.AgentProfile);
|
101
|
+
if (!agentProfile) {
|
102
|
+
agentProfile = new agent_1.AgentProfile(undefined, undefined, "You are a helpful agent", {});
|
103
|
+
}
|
104
|
+
return agentProfile;
|
105
|
+
})();
|
106
|
+
logger.debug(`agent config: ${JSON.stringify(agentProfile)}`);
|
107
|
+
// Read sudomcp config file, overriding values from command lines params.
|
108
|
+
const sudomcpConfig = tool_1.configuration.loadEnsureConfig(sudomcpConfigFile);
|
109
|
+
// Restore conversation from value or file.
|
110
|
+
const startingConversation = tool_1.utils.loadContentOrFileOrUndefined(conversationFile);
|
111
|
+
logger.debug(`startingConversation: ${JSON.stringify(startingConversation)}`);
|
112
|
+
// Run in one-shot mode or chat-mode
|
113
|
+
if (oneShot) {
|
114
|
+
if (!promptFile) {
|
115
|
+
throw "one-shot mode requires a prompt";
|
116
|
+
}
|
117
|
+
const prompt = await (0, files_1.loadFileOrStdin)(promptFile);
|
118
|
+
const { response, conversation } = await (0, agentUtils_1.runOneShot)(agentProfile, startingConversation, nodePlatform_1.NODE_PLATFORM, prompt, (0, files_1.loadImageB64OrUndefined)(imageFile), openaiApiKey, sudomcpConfig, approveToolsUpTo);
|
119
|
+
console.log(response);
|
120
|
+
if (conversationOutputFile) {
|
121
|
+
logger.debug(`writing conversation to ${conversationOutputFile}:`);
|
122
|
+
logger.debug(` conversation: ${JSON.stringify(conversation)}`);
|
123
|
+
fs.writeFileSync(conversationOutputFile, JSON.stringify(conversation));
|
124
|
+
}
|
125
|
+
}
|
126
|
+
else {
|
127
|
+
const prompt = (0, files_1.loadFileOrUndefined)(promptFile);
|
128
|
+
return (0, chat_1.runChat)(agentProfile, startingConversation, prompt, imageFile, openaiApiKey, sudomcpConfig, approveToolsUpTo);
|
129
|
+
}
|
130
|
+
},
|
131
|
+
});
|
132
|
+
function handleError(msg) {
|
133
|
+
console.error(msg);
|
134
|
+
process.exit(1);
|
135
|
+
}
|
136
|
+
(0, cmd_ts_1.run)(main, process.argv.slice(2)).catch(handleError);
|