dexto 1.0.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -12
- package/agents/agent-registry.json +53 -0
- package/agents/database-agent/README.md +35 -0
- package/agents/database-agent/data/example.db +0 -0
- package/agents/database-agent/database-agent-example.sql +98 -0
- package/agents/database-agent/database-agent.yml +116 -0
- package/agents/database-agent/setup-database.sh +64 -0
- package/agents/{agent.yml → default-agent.yml} +1 -1
- package/agents/image-editor-agent/Lenna.webp +0 -0
- package/agents/image-editor-agent/README.md +435 -0
- package/agents/image-editor-agent/image-editor-agent.yml +45 -0
- package/agents/music-agent/README.md +294 -0
- package/agents/music-agent/music-agent.yml +43 -0
- package/agents/product-name-researcher/README.md +98 -0
- package/agents/product-name-researcher/product-name-researcher.yml +161 -0
- package/agents/talk2pdf-agent/README.md +166 -0
- package/agents/talk2pdf-agent/talk2pdf-agent.yml +42 -0
- package/agents/triage-demo/README.md +337 -0
- package/agents/triage-demo/billing-agent.yml +76 -0
- package/agents/triage-demo/docs/billing-policies.md +246 -0
- package/agents/triage-demo/docs/company-overview.md +94 -0
- package/agents/triage-demo/docs/escalation-policies.md +301 -0
- package/agents/triage-demo/docs/product-features.md +253 -0
- package/agents/triage-demo/docs/technical-documentation.md +226 -0
- package/agents/triage-demo/escalation-agent.yml +82 -0
- package/agents/triage-demo/product-info-agent.yml +86 -0
- package/agents/triage-demo/technical-support-agent.yml +71 -0
- package/agents/triage-demo/test-scenarios.md +209 -0
- package/agents/triage-demo/triage-agent.yml +172 -0
- package/dist/src/app/{chunk-M4OZIDPC.js → chunk-CLDYRNV6.js} +7241 -5753
- package/dist/src/app/chunk-DYNVXGAH.js +137 -0
- package/dist/src/app/chunk-PW2PHCHR.js +83 -0
- package/dist/src/app/chunk-R4Q522DR.js +205 -0
- package/dist/src/app/chunk-UXCBS3TR.js +281 -0
- package/dist/src/app/chunk-X6LEX724.js +108 -0
- package/dist/src/app/chunk-Y33BS5SA.js +39 -0
- package/dist/src/app/{cli-confirmation-handler-2APQRKHG.js → cli-confirmation-handler-GJHPLGOL.js} +4 -1
- package/dist/src/app/errors-5MNETGOV.js +8 -0
- package/dist/src/app/index.js +2184 -612
- package/dist/src/app/loader-EFMKWNNQ.js +20 -0
- package/dist/src/app/path-7FT4SZMO.js +23 -0
- package/dist/src/app/{postgres-backend-7HVVW3RL.js → postgres-backend-U5MIIMUY.js} +12 -1
- package/dist/src/app/{redis-backend-2YBZSSSV.js → redis-backend-NGI67ILT.js} +38 -9
- package/dist/src/app/registry-RALMVM3P.js +14 -0
- package/dist/src/app/sqlite-backend-752UUBD4.js +245 -0
- package/dist/src/app/webui/.next/standalone/.next/BUILD_ID +1 -1
- package/dist/src/app/webui/.next/standalone/.next/app-build-manifest.json +20 -21
- package/dist/src/app/webui/.next/standalone/.next/app-path-routes-manifest.json +0 -1
- package/dist/src/app/webui/.next/standalone/.next/build-manifest.json +4 -4
- package/dist/src/app/webui/.next/standalone/.next/prerender-manifest.json +4 -82
- package/dist/src/app/webui/.next/standalone/.next/required-server-files.json +2 -3
- package/dist/src/app/webui/.next/standalone/.next/routes-manifest.json +0 -6
- package/dist/src/app/webui/.next/standalone/.next/server/app/_not-found/page.js +1 -1
- package/dist/src/app/webui/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/dist/src/app/webui/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/src/app/webui/.next/standalone/.next/server/app/page.js +7 -3
- package/dist/src/app/webui/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/dist/src/app/webui/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/src/app/webui/.next/standalone/.next/server/app/playground/page.js +8 -8
- package/dist/src/app/webui/.next/standalone/.next/server/app/playground/page.js.nft.json +1 -1
- package/dist/src/app/webui/.next/standalone/.next/server/app/playground/page_client-reference-manifest.js +1 -1
- package/dist/src/app/webui/.next/standalone/.next/server/app-paths-manifest.json +0 -1
- package/dist/src/app/webui/.next/standalone/.next/server/chunks/176.js +1 -0
- package/dist/src/app/webui/.next/standalone/.next/server/chunks/195.js +24 -0
- package/dist/src/app/webui/.next/standalone/.next/server/chunks/620.js +1 -0
- package/dist/src/app/webui/.next/standalone/.next/server/chunks/80.js +5 -0
- package/dist/src/app/webui/.next/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/src/app/webui/.next/standalone/.next/server/pages/500.html +1 -1
- package/dist/src/app/webui/.next/standalone/.next/server/pages-manifest.json +1 -2
- package/dist/src/app/webui/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/src/app/webui/.next/standalone/.next/server/webpack-runtime.js +1 -1
- package/dist/src/app/webui/.next/standalone/.next/static/chunks/190-b897ef36fde616bf.js +1 -0
- package/dist/src/app/webui/.next/standalone/.next/static/chunks/487-a77054bd2c64c79c.js +1 -0
- package/dist/src/app/webui/.next/standalone/.next/static/chunks/588-dbe47a44489742dd.js +1 -0
- package/dist/src/app/webui/.next/standalone/.next/static/chunks/62-35030b5cb176bd7b.js +1 -0
- package/dist/src/app/webui/.next/standalone/.next/static/chunks/app/layout-91c0cb9eb1ee327a.js +1 -0
- package/dist/src/app/webui/.next/standalone/.next/static/chunks/app/page-3279aaf14db87f45.js +1 -0
- package/dist/src/app/webui/.next/standalone/.next/static/chunks/app/playground/page-200aad53af9ca53f.js +1 -0
- package/dist/src/app/webui/.next/standalone/.next/static/css/af71306827be150e.css +3 -0
- package/dist/src/app/webui/.next/standalone/.next/static/tpCURR82LyyGfdALJ4Qvl/_buildManifest.js +1 -0
- package/dist/src/app/webui/.next/standalone/package.json +4 -3
- package/dist/src/app/webui/.next/standalone/public/favicon2.ico +0 -0
- package/dist/src/app/webui/.next/standalone/public/logo.svg +1 -0
- package/dist/src/app/webui/.next/standalone/public/logo_no_text.png +0 -0
- package/dist/src/app/webui/.next/standalone/server.js +1 -1
- package/dist/src/app/webui/.next/static/chunks/190-b897ef36fde616bf.js +1 -0
- package/dist/src/app/webui/.next/static/chunks/487-a77054bd2c64c79c.js +1 -0
- package/dist/src/app/webui/.next/static/chunks/588-dbe47a44489742dd.js +1 -0
- package/dist/src/app/webui/.next/static/chunks/62-35030b5cb176bd7b.js +1 -0
- package/dist/src/app/webui/.next/static/chunks/app/layout-91c0cb9eb1ee327a.js +1 -0
- package/dist/src/app/webui/.next/static/chunks/app/page-3279aaf14db87f45.js +1 -0
- package/dist/src/app/webui/.next/static/chunks/app/playground/page-200aad53af9ca53f.js +1 -0
- package/dist/src/app/webui/.next/static/css/af71306827be150e.css +3 -0
- package/dist/src/app/webui/.next/static/tpCURR82LyyGfdALJ4Qvl/_buildManifest.js +1 -0
- package/dist/src/app/webui/package.json +4 -3
- package/dist/src/app/webui/public/favicon2.ico +0 -0
- package/dist/src/app/webui/public/logo.svg +1 -0
- package/dist/src/app/webui/public/logo_no_text.png +0 -0
- package/dist/src/core/chunk-7A6NQKQ3.js +193 -0
- package/dist/src/core/chunk-CZIXQNMZ.js +12191 -0
- package/dist/src/core/index.cjs +10601 -8012
- package/dist/src/core/index.d.cts +4356 -3907
- package/dist/src/core/index.d.ts +4356 -3907
- package/dist/src/core/index.js +117 -8396
- package/dist/src/core/{postgres-backend-ERZ6DP76.js → postgres-backend-LOLKTD2T.js} +9 -2
- package/dist/src/core/{redis-backend-46O7Y44C.js → redis-backend-APZ576PJ.js} +35 -10
- package/dist/src/core/sqlite-backend-KQ75DPR7.js +245 -0
- package/package.json +20 -12
- package/dist/src/app/chunk-O5YHNFMH.js +0 -435
- package/dist/src/app/interactive-api-key-setup-V3GAACLN.js +0 -269
- package/dist/src/app/sqlite-backend-KIJZ5IP3.js +0 -180
- package/dist/src/app/webui/.next/standalone/.next/server/app/_not-found.html +0 -1
- package/dist/src/app/webui/.next/standalone/.next/server/app/_not-found.meta +0 -8
- package/dist/src/app/webui/.next/standalone/.next/server/app/_not-found.rsc +0 -21
- package/dist/src/app/webui/.next/standalone/.next/server/app/favicon.ico/route.js +0 -1
- package/dist/src/app/webui/.next/standalone/.next/server/app/favicon.ico/route.js.nft.json +0 -1
- package/dist/src/app/webui/.next/standalone/.next/server/app/favicon.ico.body +0 -0
- package/dist/src/app/webui/.next/standalone/.next/server/app/favicon.ico.meta +0 -1
- package/dist/src/app/webui/.next/standalone/.next/server/app/index.html +0 -1
- package/dist/src/app/webui/.next/standalone/.next/server/app/index.meta +0 -7
- package/dist/src/app/webui/.next/standalone/.next/server/app/index.rsc +0 -22
- package/dist/src/app/webui/.next/standalone/.next/server/app/playground.html +0 -1
- package/dist/src/app/webui/.next/standalone/.next/server/app/playground.meta +0 -7
- package/dist/src/app/webui/.next/standalone/.next/server/app/playground.rsc +0 -25
- package/dist/src/app/webui/.next/standalone/.next/server/chunks/447.js +0 -20
- package/dist/src/app/webui/.next/standalone/.next/server/chunks/504.js +0 -1
- package/dist/src/app/webui/.next/standalone/.next/server/chunks/54.js +0 -5
- package/dist/src/app/webui/.next/standalone/.next/server/chunks/624.js +0 -1
- package/dist/src/app/webui/.next/standalone/.next/server/chunks/985.js +0 -5
- package/dist/src/app/webui/.next/standalone/.next/server/pages/404.html +0 -1
- package/dist/src/app/webui/.next/standalone/.next/static/H-71qcBOOk528tDEa_ldn/_buildManifest.js +0 -1
- package/dist/src/app/webui/.next/standalone/.next/static/chunks/125-9b34ec01f112cdb2.js +0 -1
- package/dist/src/app/webui/.next/standalone/.next/static/chunks/487-c6ea8b63ca68db1c.js +0 -1
- package/dist/src/app/webui/.next/standalone/.next/static/chunks/588-20dc7f3a8664c387.js +0 -1
- package/dist/src/app/webui/.next/standalone/.next/static/chunks/687-3e614f30982093f6.js +0 -1
- package/dist/src/app/webui/.next/standalone/.next/static/chunks/879-cf875984faa0b72f.js +0 -1
- package/dist/src/app/webui/.next/standalone/.next/static/chunks/app/layout-ed56660b7ecaf47b.js +0 -1
- package/dist/src/app/webui/.next/standalone/.next/static/chunks/app/page-b05580de36ce0e36.js +0 -1
- package/dist/src/app/webui/.next/standalone/.next/static/chunks/app/playground/page-ac5443cddbe824aa.js +0 -1
- package/dist/src/app/webui/.next/standalone/.next/static/css/d44f09bc2605dc76.css +0 -3
- package/dist/src/app/webui/.next/standalone/node_modules/next/dist/compiled/next-server/app-route.runtime.prod.js +0 -19
- package/dist/src/app/webui/.next/standalone/public/favicon.svg +0 -26
- package/dist/src/app/webui/.next/standalone/public/logo.png +0 -0
- package/dist/src/app/webui/.next/static/H-71qcBOOk528tDEa_ldn/_buildManifest.js +0 -1
- package/dist/src/app/webui/.next/static/chunks/125-9b34ec01f112cdb2.js +0 -1
- package/dist/src/app/webui/.next/static/chunks/487-c6ea8b63ca68db1c.js +0 -1
- package/dist/src/app/webui/.next/static/chunks/588-20dc7f3a8664c387.js +0 -1
- package/dist/src/app/webui/.next/static/chunks/687-3e614f30982093f6.js +0 -1
- package/dist/src/app/webui/.next/static/chunks/879-cf875984faa0b72f.js +0 -1
- package/dist/src/app/webui/.next/static/chunks/app/layout-ed56660b7ecaf47b.js +0 -1
- package/dist/src/app/webui/.next/static/chunks/app/page-b05580de36ce0e36.js +0 -1
- package/dist/src/app/webui/.next/static/chunks/app/playground/page-ac5443cddbe824aa.js +0 -1
- package/dist/src/app/webui/.next/static/css/d44f09bc2605dc76.css +0 -3
- package/dist/src/app/webui/public/favicon.svg +0 -26
- package/dist/src/app/webui/public/logo.png +0 -0
- package/dist/src/core/chunk-BGO5B3L4.js +0 -2124
- package/dist/src/core/chunk-BYHW25EA.js +0 -41
- package/dist/src/core/sqlite-backend-RKK4WBHE.js +0 -184
- /package/dist/src/app/webui/.next/standalone/.next/static/chunks/{4bd1b696-2df85d4b3b58aed5.js → 4bd1b696-c95fa02060335229.js} +0 -0
- /package/dist/src/app/webui/.next/standalone/.next/static/chunks/{684-058c08971e023037.js → 684-2e7175657246b549.js} +0 -0
- /package/dist/src/app/webui/.next/standalone/.next/static/chunks/app/_not-found/{page-7b137d85f9de4771.js → page-b63df5a8d3225455.js} +0 -0
- /package/dist/src/app/webui/.next/standalone/.next/static/{H-71qcBOOk528tDEa_ldn → tpCURR82LyyGfdALJ4Qvl}/_ssgManifest.js +0 -0
- /package/dist/src/app/webui/.next/static/chunks/{4bd1b696-2df85d4b3b58aed5.js → 4bd1b696-c95fa02060335229.js} +0 -0
- /package/dist/src/app/webui/.next/static/chunks/{684-058c08971e023037.js → 684-2e7175657246b549.js} +0 -0
- /package/dist/src/app/webui/.next/static/chunks/app/_not-found/{page-7b137d85f9de4771.js → page-b63df5a8d3225455.js} +0 -0
- /package/dist/src/app/webui/.next/static/{H-71qcBOOk528tDEa_ldn → tpCURR82LyyGfdALJ4Qvl}/_ssgManifest.js +0 -0
package/dist/src/app/index.js
CHANGED
|
@@ -1,57 +1,181 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
getAgentRegistry
|
|
4
|
+
} from "./chunk-UXCBS3TR.js";
|
|
5
|
+
import {
|
|
6
|
+
createInitialPreferences,
|
|
7
|
+
globalPreferencesExist,
|
|
8
|
+
loadGlobalPreferences,
|
|
9
|
+
saveGlobalPreferences
|
|
10
|
+
} from "./chunk-R4Q522DR.js";
|
|
2
11
|
import {
|
|
3
12
|
AgentConfigSchema,
|
|
13
|
+
ConfigError,
|
|
4
14
|
DextoAgent,
|
|
5
|
-
DextoLLMError,
|
|
6
|
-
DextoValidationError,
|
|
7
|
-
FileNotFoundError,
|
|
8
15
|
LLMUpdatesSchema,
|
|
9
16
|
LLM_PROVIDERS,
|
|
10
17
|
LLM_REGISTRY,
|
|
11
18
|
MCPManager,
|
|
19
|
+
McpServerConfigSchema,
|
|
12
20
|
NoOpConfirmationProvider,
|
|
13
|
-
UnknownModelError,
|
|
14
|
-
UnknownProviderError,
|
|
15
|
-
addScriptsToPackageJson,
|
|
16
|
-
checkForFileInCurrentDirectory,
|
|
17
21
|
createAgentCard,
|
|
18
|
-
|
|
22
|
+
ensureDextoGlobalDirectory,
|
|
23
|
+
findDextoProjectRoot,
|
|
24
|
+
findDextoSourceRoot,
|
|
25
|
+
findPackageRoot,
|
|
19
26
|
getAllSupportedModels,
|
|
20
|
-
|
|
21
|
-
|
|
27
|
+
getDefaultModelForProvider,
|
|
28
|
+
getDextoEnvPath,
|
|
29
|
+
getDextoGlobalPath,
|
|
30
|
+
getDextoPath,
|
|
31
|
+
getExecutionContext,
|
|
32
|
+
getPrimaryApiKeyEnvVar,
|
|
22
33
|
getProviderFromModel,
|
|
34
|
+
getSupportedModels,
|
|
23
35
|
getSupportedProviders,
|
|
24
36
|
getSupportedRoutersForProvider,
|
|
25
|
-
|
|
26
|
-
|
|
37
|
+
isPath,
|
|
38
|
+
isValidProviderModel,
|
|
27
39
|
jsonSchemaToZodShape,
|
|
28
40
|
loadAgentConfig,
|
|
29
|
-
|
|
41
|
+
logger,
|
|
42
|
+
redactSensitiveData,
|
|
30
43
|
resolveAndValidateMcpServerConfig,
|
|
31
44
|
resolveApiKeyForProvider,
|
|
45
|
+
resolveBundledScript,
|
|
32
46
|
supportsBaseURL,
|
|
33
|
-
|
|
34
|
-
|
|
47
|
+
toError,
|
|
48
|
+
validateInputForLLM,
|
|
49
|
+
zodToIssues
|
|
50
|
+
} from "./chunk-CLDYRNV6.js";
|
|
51
|
+
import "./chunk-PW2PHCHR.js";
|
|
52
|
+
import "./chunk-DYNVXGAH.js";
|
|
35
53
|
import {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
54
|
+
DextoValidationError
|
|
55
|
+
} from "./chunk-X6LEX724.js";
|
|
56
|
+
import {
|
|
57
|
+
DextoRuntimeError
|
|
58
|
+
} from "./chunk-Y33BS5SA.js";
|
|
59
|
+
|
|
60
|
+
// src/core/utils/env.ts
|
|
61
|
+
import * as path from "path";
|
|
62
|
+
import { homedir } from "os";
|
|
63
|
+
import { promises as fs } from "fs";
|
|
64
|
+
import dotenv from "dotenv";
|
|
65
|
+
async function loadEnvironmentVariables(startPath = process.cwd()) {
|
|
66
|
+
const context = getExecutionContext(startPath);
|
|
67
|
+
const env = {};
|
|
68
|
+
const globalEnvPath = path.join(homedir(), ".dexto", ".env");
|
|
69
|
+
try {
|
|
70
|
+
const globalResult = dotenv.config({ path: globalEnvPath, processEnv: {} });
|
|
71
|
+
if (globalResult.parsed) {
|
|
72
|
+
Object.assign(env, globalResult.parsed);
|
|
73
|
+
}
|
|
74
|
+
} catch {
|
|
75
|
+
}
|
|
76
|
+
if (context === "dexto-source" || context === "dexto-project") {
|
|
77
|
+
const projectEnvPath = getDextoEnvPath(startPath);
|
|
78
|
+
try {
|
|
79
|
+
const projectResult = dotenv.config({ path: projectEnvPath, processEnv: {} });
|
|
80
|
+
if (projectResult.parsed) {
|
|
81
|
+
Object.assign(env, projectResult.parsed);
|
|
82
|
+
}
|
|
83
|
+
} catch {
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
for (const [key, value] of Object.entries(process.env)) {
|
|
87
|
+
if (value !== void 0 && value !== "") {
|
|
88
|
+
env[key] = value;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return env;
|
|
92
|
+
}
|
|
93
|
+
async function applyLayeredEnvironmentLoading(startPath = process.cwd()) {
|
|
94
|
+
await ensureDextoGlobalDirectory();
|
|
95
|
+
const layeredEnv = await loadEnvironmentVariables(startPath);
|
|
96
|
+
Object.assign(process.env, layeredEnv);
|
|
97
|
+
}
|
|
98
|
+
async function updateEnvFile(envFilePath, updates) {
|
|
99
|
+
const dextoEnvKeys = [
|
|
100
|
+
"OPENAI_API_KEY",
|
|
101
|
+
"ANTHROPIC_API_KEY",
|
|
102
|
+
"GOOGLE_GENERATIVE_AI_API_KEY",
|
|
103
|
+
"GROQ_API_KEY",
|
|
104
|
+
"DEXTO_LOG_LEVEL"
|
|
105
|
+
];
|
|
106
|
+
await fs.mkdir(path.dirname(envFilePath), { recursive: true });
|
|
107
|
+
let envLines = [];
|
|
108
|
+
try {
|
|
109
|
+
const existingEnv = await fs.readFile(envFilePath, "utf8");
|
|
110
|
+
envLines = existingEnv.split("\n");
|
|
111
|
+
} catch {
|
|
112
|
+
}
|
|
113
|
+
const currentValues = {};
|
|
114
|
+
envLines.forEach((line) => {
|
|
115
|
+
const match = line.match(/^([A-Z0-9_]+)=(.*)$/);
|
|
116
|
+
if (match && match[1] && match[2] !== void 0 && dextoEnvKeys.includes(match[1])) {
|
|
117
|
+
currentValues[match[1]] = match[2];
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
const updatedValues = {
|
|
121
|
+
OPENAI_API_KEY: updates.OPENAI_API_KEY ?? currentValues.OPENAI_API_KEY ?? "",
|
|
122
|
+
ANTHROPIC_API_KEY: updates.ANTHROPIC_API_KEY ?? currentValues.ANTHROPIC_API_KEY ?? "",
|
|
123
|
+
GOOGLE_GENERATIVE_AI_API_KEY: updates.GOOGLE_GENERATIVE_AI_API_KEY ?? currentValues.GOOGLE_GENERATIVE_AI_API_KEY ?? "",
|
|
124
|
+
GROQ_API_KEY: updates.GROQ_API_KEY ?? currentValues.GROQ_API_KEY ?? "",
|
|
125
|
+
DEXTO_LOG_LEVEL: updates.DEXTO_LOG_LEVEL ?? currentValues.DEXTO_LOG_LEVEL ?? "info"
|
|
126
|
+
};
|
|
127
|
+
const sectionHeader = "## Dexto env variables";
|
|
128
|
+
const headerIndex = envLines.findIndex((line) => line.trim() === sectionHeader);
|
|
129
|
+
let contentLines;
|
|
130
|
+
if (headerIndex !== -1) {
|
|
131
|
+
const beforeSection = envLines.slice(0, headerIndex);
|
|
132
|
+
let sectionEnd = headerIndex + 1;
|
|
133
|
+
while (sectionEnd < envLines.length && envLines[sectionEnd]?.trim() !== "") {
|
|
134
|
+
sectionEnd++;
|
|
135
|
+
}
|
|
136
|
+
if (sectionEnd < envLines.length && envLines[sectionEnd]?.trim() === "") {
|
|
137
|
+
sectionEnd++;
|
|
138
|
+
}
|
|
139
|
+
const afterSection = envLines.slice(sectionEnd);
|
|
140
|
+
contentLines = [...beforeSection, ...afterSection];
|
|
141
|
+
} else {
|
|
142
|
+
contentLines = envLines;
|
|
143
|
+
}
|
|
144
|
+
const existingEnvVars = {};
|
|
145
|
+
contentLines.forEach((line) => {
|
|
146
|
+
const match = line.match(/^([A-Z0-9_]+)=(.*)$/);
|
|
147
|
+
if (match && match[1] && match[2] !== void 0 && dextoEnvKeys.includes(match[1])) {
|
|
148
|
+
existingEnvVars[match[1]] = match[2];
|
|
149
|
+
}
|
|
150
|
+
});
|
|
151
|
+
if (contentLines.length > 0) {
|
|
152
|
+
if (contentLines[contentLines.length - 1]?.trim() !== "") {
|
|
153
|
+
contentLines.push("");
|
|
154
|
+
}
|
|
155
|
+
} else {
|
|
156
|
+
contentLines.push("");
|
|
157
|
+
}
|
|
158
|
+
contentLines.push(sectionHeader);
|
|
159
|
+
for (const key of dextoEnvKeys) {
|
|
160
|
+
if (key in existingEnvVars && !(key in updates)) {
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
contentLines.push(`${key}=${updatedValues[key]}`);
|
|
164
|
+
}
|
|
165
|
+
contentLines.push("");
|
|
166
|
+
await fs.writeFile(envFilePath, contentLines.join("\n"), "utf8");
|
|
167
|
+
}
|
|
43
168
|
|
|
44
169
|
// src/app/index.ts
|
|
45
|
-
import
|
|
46
|
-
import { existsSync as existsSync2 } from "fs";
|
|
170
|
+
import { existsSync as existsSync4 } from "fs";
|
|
47
171
|
import { Command } from "commander";
|
|
48
|
-
import * as
|
|
49
|
-
import
|
|
172
|
+
import * as p6 from "@clack/prompts";
|
|
173
|
+
import chalk23 from "chalk";
|
|
50
174
|
|
|
51
175
|
// package.json
|
|
52
176
|
var package_default = {
|
|
53
177
|
name: "dexto",
|
|
54
|
-
version: "1.0
|
|
178
|
+
version: "1.1.0",
|
|
55
179
|
engines: {
|
|
56
180
|
node: ">=20.0.0",
|
|
57
181
|
npm: ">=8.3.0"
|
|
@@ -107,27 +231,35 @@ var package_default = {
|
|
|
107
231
|
"cli",
|
|
108
232
|
"natural-language",
|
|
109
233
|
"openai",
|
|
110
|
-
"truffle-ai"
|
|
234
|
+
"truffle-ai",
|
|
235
|
+
"dexto"
|
|
111
236
|
],
|
|
112
237
|
author: "",
|
|
113
238
|
license: "Elastic-2.0",
|
|
114
239
|
files: [
|
|
115
240
|
"dist/",
|
|
116
|
-
"agents/agent.
|
|
241
|
+
"agents/agent-registry.json",
|
|
117
242
|
"agents/agent-template.yml",
|
|
243
|
+
"agents/default-agent.yml",
|
|
244
|
+
"agents/database-agent/",
|
|
245
|
+
"agents/talk2pdf-agent/",
|
|
246
|
+
"agents/image-editor-agent/",
|
|
247
|
+
"agents/music-agent/",
|
|
248
|
+
"agents/product-name-researcher/",
|
|
249
|
+
"agents/triage-demo/",
|
|
118
250
|
"public/"
|
|
119
251
|
],
|
|
120
252
|
dependencies: {
|
|
121
|
-
"@ai-sdk/anthropic": "^
|
|
122
|
-
"@ai-sdk/cohere": "^
|
|
123
|
-
"@ai-sdk/google": "^
|
|
124
|
-
"@ai-sdk/groq": "^
|
|
125
|
-
"@ai-sdk/openai": "^
|
|
126
|
-
"@ai-sdk/xai": "^
|
|
253
|
+
"@ai-sdk/anthropic": "^2.0.0",
|
|
254
|
+
"@ai-sdk/cohere": "^2.0.0",
|
|
255
|
+
"@ai-sdk/google": "^2.0.0",
|
|
256
|
+
"@ai-sdk/groq": "^2.0.0",
|
|
257
|
+
"@ai-sdk/openai": "^2.0.0",
|
|
258
|
+
"@ai-sdk/xai": "^2.0.0",
|
|
127
259
|
"@anthropic-ai/sdk": "^0.39.0",
|
|
128
260
|
"@clack/prompts": "^0.10.1",
|
|
129
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
130
|
-
ai: "^
|
|
261
|
+
"@modelcontextprotocol/sdk": "^1.17.2",
|
|
262
|
+
ai: "^5.0.0",
|
|
131
263
|
"better-sqlite3": "^11.10.0",
|
|
132
264
|
boxen: "^7.1.1",
|
|
133
265
|
chalk: "^5.4.1",
|
|
@@ -150,7 +282,7 @@ var package_default = {
|
|
|
150
282
|
winston: "^3.17.0",
|
|
151
283
|
ws: "^8.18.1",
|
|
152
284
|
yaml: "^2.7.1",
|
|
153
|
-
zod: "^3.
|
|
285
|
+
zod: "^3.25.0",
|
|
154
286
|
"zod-to-json-schema": "^3.24.6"
|
|
155
287
|
},
|
|
156
288
|
devDependencies: {
|
|
@@ -186,25 +318,100 @@ var package_default = {
|
|
|
186
318
|
}
|
|
187
319
|
};
|
|
188
320
|
|
|
189
|
-
// src/
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
321
|
+
// src/core/config/agent-resolver.ts
|
|
322
|
+
import { promises as fs2 } from "fs";
|
|
323
|
+
import path2 from "path";
|
|
324
|
+
async function resolveAgentPath(nameOrPath, autoInstall = true, injectPreferences = true) {
|
|
325
|
+
if (nameOrPath && isPath(nameOrPath)) {
|
|
326
|
+
const resolved = path2.resolve(nameOrPath);
|
|
327
|
+
try {
|
|
328
|
+
const stat = await fs2.stat(resolved);
|
|
329
|
+
if (!stat.isFile()) {
|
|
330
|
+
throw ConfigError.fileNotFound(resolved);
|
|
331
|
+
}
|
|
332
|
+
return resolved;
|
|
333
|
+
} catch {
|
|
334
|
+
throw ConfigError.fileNotFound(resolved);
|
|
335
|
+
}
|
|
193
336
|
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
337
|
+
if (nameOrPath) {
|
|
338
|
+
const { getAgentRegistry: getAgentRegistry2 } = await import("./registry-RALMVM3P.js");
|
|
339
|
+
const registry = getAgentRegistry2();
|
|
340
|
+
return await registry.resolveAgent(nameOrPath, autoInstall, injectPreferences);
|
|
197
341
|
}
|
|
198
|
-
|
|
199
|
-
|
|
342
|
+
return await resolveDefaultAgentByContext(autoInstall, injectPreferences);
|
|
343
|
+
}
|
|
344
|
+
async function resolveDefaultAgentByContext(autoInstall = true, injectPreferences = true) {
|
|
345
|
+
const executionContext = getExecutionContext();
|
|
346
|
+
switch (executionContext) {
|
|
347
|
+
case "dexto-source":
|
|
348
|
+
return await resolveDefaultAgentForDextoSource();
|
|
349
|
+
case "dexto-project":
|
|
350
|
+
return await resolveDefaultAgentForDextoProject(autoInstall, injectPreferences);
|
|
351
|
+
case "global-cli":
|
|
352
|
+
return await resolveDefaultAgentForGlobalCLI(autoInstall, injectPreferences);
|
|
353
|
+
default:
|
|
354
|
+
throw ConfigError.unknownContext(executionContext);
|
|
200
355
|
}
|
|
201
|
-
|
|
202
|
-
|
|
356
|
+
}
|
|
357
|
+
async function resolveDefaultAgentForDextoSource() {
|
|
358
|
+
logger.debug("Resolving default agent for dexto source context");
|
|
359
|
+
const sourceRoot = findDextoSourceRoot();
|
|
360
|
+
if (!sourceRoot) {
|
|
361
|
+
throw ConfigError.bundledNotFound("dexto source directory not found");
|
|
203
362
|
}
|
|
204
|
-
|
|
205
|
-
|
|
363
|
+
const bundledPath = path2.join(sourceRoot, "agents", "default-agent.yml");
|
|
364
|
+
try {
|
|
365
|
+
await fs2.access(bundledPath);
|
|
366
|
+
return bundledPath;
|
|
367
|
+
} catch {
|
|
368
|
+
throw ConfigError.bundledNotFound(bundledPath);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
async function resolveDefaultAgentForDextoProject(autoInstall = true, injectPreferences = true) {
|
|
372
|
+
logger.debug("Resolving default agent for dexto project context");
|
|
373
|
+
const projectRoot = findDextoProjectRoot();
|
|
374
|
+
if (!projectRoot) {
|
|
375
|
+
throw ConfigError.unknownContext("dexto-project: project root not found");
|
|
376
|
+
}
|
|
377
|
+
const candidatePaths = [
|
|
378
|
+
path2.join(projectRoot, "default-agent.yml"),
|
|
379
|
+
path2.join(projectRoot, "agents", "default-agent.yml"),
|
|
380
|
+
path2.join(projectRoot, "src", "dexto", "agents", "default-agent.yml")
|
|
381
|
+
];
|
|
382
|
+
for (const p7 of candidatePaths) {
|
|
383
|
+
try {
|
|
384
|
+
await fs2.access(p7);
|
|
385
|
+
return p7;
|
|
386
|
+
} catch {
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
logger.debug(`No project-local default-agent.yml found in ${projectRoot}`);
|
|
390
|
+
if (!globalPreferencesExist()) {
|
|
391
|
+
throw ConfigError.noProjectDefault(projectRoot);
|
|
392
|
+
}
|
|
393
|
+
const preferences = await loadGlobalPreferences();
|
|
394
|
+
if (!preferences.setup.completed) {
|
|
395
|
+
throw ConfigError.setupIncomplete();
|
|
206
396
|
}
|
|
207
|
-
|
|
397
|
+
const preferredAgentName = preferences.defaults.defaultAgent;
|
|
398
|
+
const { getAgentRegistry: getAgentRegistry2 } = await import("./registry-RALMVM3P.js");
|
|
399
|
+
const registry = getAgentRegistry2();
|
|
400
|
+
return await registry.resolveAgent(preferredAgentName, autoInstall, injectPreferences);
|
|
401
|
+
}
|
|
402
|
+
async function resolveDefaultAgentForGlobalCLI(autoInstall = true, injectPreferences = true) {
|
|
403
|
+
logger.debug("Resolving default agent for global CLI context");
|
|
404
|
+
if (!globalPreferencesExist()) {
|
|
405
|
+
throw ConfigError.noGlobalPreferences();
|
|
406
|
+
}
|
|
407
|
+
const preferences = await loadGlobalPreferences();
|
|
408
|
+
if (!preferences.setup.completed) {
|
|
409
|
+
throw ConfigError.setupIncomplete();
|
|
410
|
+
}
|
|
411
|
+
const preferredAgentName = preferences.defaults.defaultAgent;
|
|
412
|
+
const { getAgentRegistry: getAgentRegistry2 } = await import("./registry-RALMVM3P.js");
|
|
413
|
+
const registry = getAgentRegistry2();
|
|
414
|
+
return await registry.resolveAgent(preferredAgentName, autoInstall, injectPreferences);
|
|
208
415
|
}
|
|
209
416
|
|
|
210
417
|
// src/app/cli/cli.ts
|
|
@@ -219,7 +426,11 @@ var CLISubscriber = class {
|
|
|
219
426
|
currentLines = 0;
|
|
220
427
|
subscribe(eventBus) {
|
|
221
428
|
eventBus.on("llmservice:thinking", this.onThinking.bind(this));
|
|
222
|
-
eventBus.on("llmservice:chunk", (payload) =>
|
|
429
|
+
eventBus.on("llmservice:chunk", (payload) => {
|
|
430
|
+
if (payload.type === "text") {
|
|
431
|
+
this.onChunk(payload.content);
|
|
432
|
+
}
|
|
433
|
+
});
|
|
223
434
|
eventBus.on(
|
|
224
435
|
"llmservice:toolCall",
|
|
225
436
|
(payload) => this.onToolCall(payload.toolName, payload.args)
|
|
@@ -248,8 +459,8 @@ var CLISubscriber = class {
|
|
|
248
459
|
onThinking() {
|
|
249
460
|
logger.info("AI thinking...", null, "yellow");
|
|
250
461
|
}
|
|
251
|
-
onChunk(
|
|
252
|
-
this.accumulatedResponse +=
|
|
462
|
+
onChunk(text3) {
|
|
463
|
+
this.accumulatedResponse += text3;
|
|
253
464
|
const box = boxen(chalk.white(this.accumulatedResponse), {
|
|
254
465
|
padding: 1,
|
|
255
466
|
borderColor: "yellow",
|
|
@@ -270,15 +481,28 @@ var CLISubscriber = class {
|
|
|
270
481
|
onToolResult(toolName, result) {
|
|
271
482
|
logger.toolResult(result);
|
|
272
483
|
}
|
|
273
|
-
onResponse(
|
|
484
|
+
onResponse(text3) {
|
|
274
485
|
this.accumulatedResponse = "";
|
|
275
486
|
this.currentLines = 0;
|
|
276
|
-
logger.displayAIResponse({ content:
|
|
487
|
+
logger.displayAIResponse({ content: text3 });
|
|
277
488
|
}
|
|
278
489
|
onError(error) {
|
|
279
490
|
this.accumulatedResponse = "";
|
|
280
491
|
this.currentLines = 0;
|
|
281
|
-
logger.
|
|
492
|
+
logger.displayError(error.message, error);
|
|
493
|
+
if (logger.getLevel() === "debug") {
|
|
494
|
+
logger.error(
|
|
495
|
+
`\u274C Error: ${error.message}`,
|
|
496
|
+
{
|
|
497
|
+
stack: error.stack,
|
|
498
|
+
name: error.name,
|
|
499
|
+
cause: error.cause
|
|
500
|
+
},
|
|
501
|
+
"red"
|
|
502
|
+
);
|
|
503
|
+
} else {
|
|
504
|
+
logger.error(`\u274C Error: ${error.message}`, null, "red");
|
|
505
|
+
}
|
|
282
506
|
}
|
|
283
507
|
onConversationReset() {
|
|
284
508
|
this.accumulatedResponse = "";
|
|
@@ -287,7 +511,7 @@ var CLISubscriber = class {
|
|
|
287
511
|
}
|
|
288
512
|
};
|
|
289
513
|
|
|
290
|
-
// src/app/cli/interactive-commands/command-parser.ts
|
|
514
|
+
// src/app/cli/commands/interactive-commands/command-parser.ts
|
|
291
515
|
import chalk2 from "chalk";
|
|
292
516
|
function parseQuotedArguments(input) {
|
|
293
517
|
const args = [];
|
|
@@ -401,7 +625,7 @@ function displayAllCommands(commands) {
|
|
|
401
625
|
console.log(chalk2.dim("\u{1F4A1} Tip: Type your message normally (without /) to chat with the AI\n"));
|
|
402
626
|
}
|
|
403
627
|
|
|
404
|
-
// src/app/cli/interactive-commands/general-commands.ts
|
|
628
|
+
// src/app/cli/commands/interactive-commands/general-commands.ts
|
|
405
629
|
import chalk3 from "chalk";
|
|
406
630
|
function createHelpCommand(getAllCommands) {
|
|
407
631
|
return {
|
|
@@ -490,10 +714,10 @@ var generalCommands = [
|
|
|
490
714
|
}
|
|
491
715
|
];
|
|
492
716
|
|
|
493
|
-
// src/app/cli/interactive-commands/session/session-commands.ts
|
|
717
|
+
// src/app/cli/commands/interactive-commands/session/session-commands.ts
|
|
494
718
|
import chalk5 from "chalk";
|
|
495
719
|
|
|
496
|
-
// src/app/cli/interactive-commands/session/helpers/formatters.ts
|
|
720
|
+
// src/app/cli/commands/interactive-commands/session/helpers/formatters.ts
|
|
497
721
|
import chalk4 from "chalk";
|
|
498
722
|
function formatSessionInfo(sessionId, metadata, isCurrent = false) {
|
|
499
723
|
const prefix = isCurrent ? chalk4.green("\u2192") : " ";
|
|
@@ -557,7 +781,7 @@ function formatHistoryMessage(message, index) {
|
|
|
557
781
|
return ` ${chalk4.dim(timestamp)} ${roleColor.bold(displayLabel)}: ${content}${toolInfo}`;
|
|
558
782
|
}
|
|
559
783
|
|
|
560
|
-
// src/app/cli/interactive-commands/session/session-commands.ts
|
|
784
|
+
// src/app/cli/commands/interactive-commands/session/session-commands.ts
|
|
561
785
|
async function getCurrentSessionInfo(agent) {
|
|
562
786
|
const currentId = agent.getCurrentSessionId();
|
|
563
787
|
const metadata = await agent.getSessionMetadata(currentId);
|
|
@@ -603,13 +827,30 @@ var sessionCommand = {
|
|
|
603
827
|
);
|
|
604
828
|
return true;
|
|
605
829
|
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
830
|
+
const entries = await Promise.all(
|
|
831
|
+
sessionIds.map(async (id) => {
|
|
832
|
+
try {
|
|
833
|
+
const metadata = await agent.getSessionMetadata(id);
|
|
834
|
+
return { id, metadata };
|
|
835
|
+
} catch (e) {
|
|
836
|
+
logger.error(
|
|
837
|
+
`Failed to fetch metadata for session ${id}: ${e instanceof Error ? e.message : String(e)}`
|
|
838
|
+
);
|
|
839
|
+
return { id, metadata: void 0 };
|
|
840
|
+
}
|
|
841
|
+
})
|
|
842
|
+
);
|
|
843
|
+
let displayed = 0;
|
|
844
|
+
for (const { id, metadata } of entries) {
|
|
845
|
+
if (!metadata) continue;
|
|
846
|
+
const isCurrent = id === current.id;
|
|
847
|
+
console.log(` ${formatSessionInfo(id, metadata, isCurrent)}`);
|
|
848
|
+
displayed++;
|
|
610
849
|
}
|
|
611
|
-
console.log(
|
|
612
|
-
|
|
850
|
+
console.log(
|
|
851
|
+
chalk5.dim(`
|
|
852
|
+
Total: ${displayed} of ${sessionIds.length} sessions`)
|
|
853
|
+
);
|
|
613
854
|
console.log(chalk5.dim(" \u{1F4A1} Use /session switch <id> to change sessions\n"));
|
|
614
855
|
} catch (error) {
|
|
615
856
|
logger.error(
|
|
@@ -919,10 +1160,10 @@ var searchCommand = {
|
|
|
919
1160
|
}
|
|
920
1161
|
};
|
|
921
1162
|
|
|
922
|
-
// src/app/cli/interactive-commands/session/index.ts
|
|
1163
|
+
// src/app/cli/commands/interactive-commands/session/index.ts
|
|
923
1164
|
var sessionCommands = [sessionCommand, historyCommand, searchCommand];
|
|
924
1165
|
|
|
925
|
-
// src/app/cli/interactive-commands/model/model-commands.ts
|
|
1166
|
+
// src/app/cli/commands/interactive-commands/model/model-commands.ts
|
|
926
1167
|
import chalk6 from "chalk";
|
|
927
1168
|
var modelCommands = {
|
|
928
1169
|
name: "model",
|
|
@@ -1016,20 +1257,21 @@ var modelCommands = {
|
|
|
1016
1257
|
await agent.switchLLM(llmConfig);
|
|
1017
1258
|
console.log(chalk6.green(`\u2705 Successfully switched to ${model} (${provider})`));
|
|
1018
1259
|
} catch (error) {
|
|
1019
|
-
if (error instanceof
|
|
1260
|
+
if (error instanceof DextoRuntimeError) {
|
|
1020
1261
|
console.log(chalk6.red("\u274C Failed to switch model:"));
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
);
|
|
1028
|
-
if (warnings.length > 0) {
|
|
1029
|
-
for (const warning of warnings) {
|
|
1030
|
-
console.log(chalk6.yellow(`\u26A0\uFE0F ${warning.message}`));
|
|
1262
|
+
console.log(chalk6.red(` ${error.message}`));
|
|
1263
|
+
console.log(chalk6.dim(` Code: ${error.code}`));
|
|
1264
|
+
if (error.recovery) {
|
|
1265
|
+
const recoverySteps = Array.isArray(error.recovery) ? error.recovery : [error.recovery];
|
|
1266
|
+
for (const step of recoverySteps) {
|
|
1267
|
+
console.log(chalk6.blue(`\u{1F4A1} ${step}`));
|
|
1031
1268
|
}
|
|
1032
1269
|
}
|
|
1270
|
+
} else if (error instanceof DextoValidationError) {
|
|
1271
|
+
console.log(chalk6.red("\u274C Validation failed:"));
|
|
1272
|
+
error.errors.forEach((err) => {
|
|
1273
|
+
console.log(chalk6.red(` - ${err.message}`));
|
|
1274
|
+
});
|
|
1033
1275
|
} else {
|
|
1034
1276
|
logger.error(
|
|
1035
1277
|
`Failed to switch model: ${error instanceof Error ? error.message : String(error)}`
|
|
@@ -1091,10 +1333,10 @@ var modelCommands = {
|
|
|
1091
1333
|
}
|
|
1092
1334
|
};
|
|
1093
1335
|
|
|
1094
|
-
// src/app/cli/interactive-commands/mcp/mcp-commands.ts
|
|
1336
|
+
// src/app/cli/commands/interactive-commands/mcp/mcp-commands.ts
|
|
1095
1337
|
import chalk8 from "chalk";
|
|
1096
1338
|
|
|
1097
|
-
// src/app/cli/interactive-commands/utils/arg-parser.ts
|
|
1339
|
+
// src/app/cli/commands/interactive-commands/utils/arg-parser.ts
|
|
1098
1340
|
function parseOptions(args) {
|
|
1099
1341
|
const parsedArgs = [];
|
|
1100
1342
|
const options = {};
|
|
@@ -1116,7 +1358,7 @@ function parseOptions(args) {
|
|
|
1116
1358
|
return { parsedArgs, options, flags };
|
|
1117
1359
|
}
|
|
1118
1360
|
|
|
1119
|
-
// src/app/cli/interactive-commands/mcp/mcp-add-utils.ts
|
|
1361
|
+
// src/app/cli/commands/interactive-commands/mcp/mcp-add-utils.ts
|
|
1120
1362
|
import chalk7 from "chalk";
|
|
1121
1363
|
function parseStdioArgs(args) {
|
|
1122
1364
|
const errors = [];
|
|
@@ -1276,7 +1518,7 @@ function validateAndShowErrors(serverName, config, existingServers = []) {
|
|
|
1276
1518
|
return true;
|
|
1277
1519
|
}
|
|
1278
1520
|
|
|
1279
|
-
// src/app/cli/interactive-commands/mcp/mcp-commands.ts
|
|
1521
|
+
// src/app/cli/commands/interactive-commands/mcp/mcp-commands.ts
|
|
1280
1522
|
async function handleMcpAddStdio(args, agent) {
|
|
1281
1523
|
const { serverName, config, errors } = parseStdioArgs(args);
|
|
1282
1524
|
if (errors.length > 0) {
|
|
@@ -1555,7 +1797,7 @@ var mcpCommands = {
|
|
|
1555
1797
|
}
|
|
1556
1798
|
};
|
|
1557
1799
|
|
|
1558
|
-
// src/app/cli/interactive-commands/system/system-commands.ts
|
|
1800
|
+
// src/app/cli/commands/interactive-commands/system/system-commands.ts
|
|
1559
1801
|
import chalk9 from "chalk";
|
|
1560
1802
|
var systemCommands = [
|
|
1561
1803
|
{
|
|
@@ -1670,7 +1912,7 @@ Current log level: ${chalk9.cyan(logger.getLevel())}`));
|
|
|
1670
1912
|
}
|
|
1671
1913
|
];
|
|
1672
1914
|
|
|
1673
|
-
// src/app/cli/interactive-commands/tool-commands.ts
|
|
1915
|
+
// src/app/cli/commands/interactive-commands/tool-commands.ts
|
|
1674
1916
|
import chalk10 from "chalk";
|
|
1675
1917
|
var toolCommands = [
|
|
1676
1918
|
{
|
|
@@ -1718,7 +1960,7 @@ var toolCommands = [
|
|
|
1718
1960
|
}
|
|
1719
1961
|
];
|
|
1720
1962
|
|
|
1721
|
-
// src/app/cli/interactive-commands/prompt-commands.ts
|
|
1963
|
+
// src/app/cli/commands/interactive-commands/prompt-commands.ts
|
|
1722
1964
|
import chalk11 from "chalk";
|
|
1723
1965
|
var promptCommands = [
|
|
1724
1966
|
{
|
|
@@ -1744,7 +1986,7 @@ var promptCommands = [
|
|
|
1744
1986
|
}
|
|
1745
1987
|
];
|
|
1746
1988
|
|
|
1747
|
-
// src/app/cli/interactive-commands/documentation-commands.ts
|
|
1989
|
+
// src/app/cli/commands/interactive-commands/documentation-commands.ts
|
|
1748
1990
|
import chalk12 from "chalk";
|
|
1749
1991
|
var documentationCommands = [
|
|
1750
1992
|
{
|
|
@@ -1754,29 +1996,25 @@ var documentationCommands = [
|
|
|
1754
1996
|
category: "Documentation",
|
|
1755
1997
|
aliases: ["doc"],
|
|
1756
1998
|
handler: async (_args, _agent) => {
|
|
1999
|
+
const docsUrl = "https://docs.dexto.ai/category/getting-started/";
|
|
1757
2000
|
try {
|
|
1758
|
-
const { spawn:
|
|
1759
|
-
|
|
1760
|
-
console.log(chalk12.blue(`\u{1F310} Opening Dexto documentation: ${url}`));
|
|
2001
|
+
const { spawn: spawn3 } = await import("child_process");
|
|
2002
|
+
console.log(chalk12.blue(`\u{1F310} Opening Dexto documentation: ${docsUrl}`));
|
|
1761
2003
|
const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
1762
|
-
|
|
2004
|
+
spawn3(command, [docsUrl], { detached: true, stdio: "ignore" });
|
|
1763
2005
|
console.log(chalk12.green("\u2705 Documentation opened in browser"));
|
|
1764
2006
|
} catch (error) {
|
|
1765
2007
|
logger.error(
|
|
1766
2008
|
`Failed to open documentation: ${error instanceof Error ? error.message : String(error)}`
|
|
1767
2009
|
);
|
|
1768
|
-
console.log(
|
|
1769
|
-
chalk12.yellow(
|
|
1770
|
-
"\u{1F4A1} You can manually visit: https://truffle-ai.github.io/dexto/docs/category/getting-started/"
|
|
1771
|
-
)
|
|
1772
|
-
);
|
|
2010
|
+
console.log(chalk12.yellow(`\u{1F4A1} You can manually visit: ${docsUrl}`));
|
|
1773
2011
|
}
|
|
1774
2012
|
return true;
|
|
1775
2013
|
}
|
|
1776
2014
|
}
|
|
1777
2015
|
];
|
|
1778
2016
|
|
|
1779
|
-
// src/app/cli/interactive-commands/commands.ts
|
|
2017
|
+
// src/app/cli/commands/interactive-commands/commands.ts
|
|
1780
2018
|
var CLI_COMMANDS = [];
|
|
1781
2019
|
var baseCommands = [
|
|
1782
2020
|
// General commands (without help)
|
|
@@ -1893,25 +2131,48 @@ async function loadMostRecentSession(agent) {
|
|
|
1893
2131
|
async function _initCli(agent) {
|
|
1894
2132
|
await loadMostRecentSession(agent);
|
|
1895
2133
|
registerGracefulShutdown(agent);
|
|
1896
|
-
|
|
1897
|
-
|
|
2134
|
+
const llmConfig = agent.getCurrentLLMConfig();
|
|
2135
|
+
const connectedServers = agent.mcpManager.getClients();
|
|
1898
2136
|
const failedConnections = agent.mcpManager.getFailedConnections();
|
|
2137
|
+
const currentSessionId = agent.getCurrentSessionId();
|
|
2138
|
+
let toolStats;
|
|
2139
|
+
try {
|
|
2140
|
+
toolStats = await agent.toolManager.getToolStats();
|
|
2141
|
+
} catch (error) {
|
|
2142
|
+
logger.error(
|
|
2143
|
+
`Failed to load tools: ${error instanceof Error ? error.message : String(error)}`
|
|
2144
|
+
);
|
|
2145
|
+
}
|
|
2146
|
+
const startupInfo = {
|
|
2147
|
+
model: llmConfig.model,
|
|
2148
|
+
provider: llmConfig.provider,
|
|
2149
|
+
connectedServers: {
|
|
2150
|
+
count: connectedServers.size,
|
|
2151
|
+
names: Array.from(connectedServers.keys())
|
|
2152
|
+
},
|
|
2153
|
+
sessionId: currentSessionId,
|
|
2154
|
+
logLevel: logger.getLevel()
|
|
2155
|
+
};
|
|
1899
2156
|
if (Object.keys(failedConnections).length > 0) {
|
|
1900
|
-
|
|
2157
|
+
startupInfo.failedConnections = failedConnections;
|
|
2158
|
+
}
|
|
2159
|
+
if (toolStats) {
|
|
2160
|
+
startupInfo.toolStats = toolStats;
|
|
1901
2161
|
}
|
|
2162
|
+
const logFile = logger.getLogFilePath();
|
|
2163
|
+
if (logFile) {
|
|
2164
|
+
startupInfo.logFile = logFile;
|
|
2165
|
+
}
|
|
2166
|
+
logger.displayStartupInfo(startupInfo);
|
|
2167
|
+
logger.debug(`Startup configuration: ${JSON.stringify(startupInfo, null, 2)}`);
|
|
1902
2168
|
logger.info("Setting up CLI event subscriptions...");
|
|
1903
2169
|
const cliSubscriber = new CLISubscriber();
|
|
1904
2170
|
cliSubscriber.subscribe(agent.agentEventBus);
|
|
1905
2171
|
logger.info("Loading available tools...");
|
|
1906
|
-
|
|
1907
|
-
const toolStats = await agent.toolManager.getToolStats();
|
|
2172
|
+
if (toolStats) {
|
|
1908
2173
|
logger.info(
|
|
1909
2174
|
`Loaded ${toolStats.total} total tools: ${toolStats.mcp} MCP, ${toolStats.internal} internal`
|
|
1910
2175
|
);
|
|
1911
|
-
} catch (error) {
|
|
1912
|
-
logger.error(
|
|
1913
|
-
`Failed to load tools: ${error instanceof Error ? error.message : String(error)}`
|
|
1914
|
-
);
|
|
1915
2176
|
}
|
|
1916
2177
|
logger.info(`CLI initialized successfully. Ready for input.`, null, "green");
|
|
1917
2178
|
console.log(chalk13.bold.cyan("\n\u{1F680} Welcome to Dexto CLI!"));
|
|
@@ -1999,11 +2260,14 @@ async function startHeadlessCli(agent, prompt) {
|
|
|
1999
2260
|
await agent.run(prompt);
|
|
2000
2261
|
}
|
|
2001
2262
|
} catch (error) {
|
|
2002
|
-
if (error instanceof
|
|
2003
|
-
logger.error(`
|
|
2004
|
-
} else if (error instanceof
|
|
2005
|
-
logger.error(`
|
|
2006
|
-
|
|
2263
|
+
if (error instanceof DextoRuntimeError && error.code === "llm_model_unknown" /* MODEL_UNKNOWN */) {
|
|
2264
|
+
logger.error(`LLM error: ${error.message}`, null, "red");
|
|
2265
|
+
} else if (error instanceof DextoValidationError) {
|
|
2266
|
+
logger.error(`Validation failed:`, null, "red");
|
|
2267
|
+
error.errors.forEach((err) => {
|
|
2268
|
+
logger.error(` - ${err.message}`, null, "red");
|
|
2269
|
+
});
|
|
2270
|
+
} else if (error instanceof DextoRuntimeError && error.scope === "config" /* CONFIG */) {
|
|
2007
2271
|
logger.error(`Configuration error: ${error.message}`, null, "red");
|
|
2008
2272
|
} else {
|
|
2009
2273
|
logger.error(
|
|
@@ -2063,7 +2327,8 @@ var WebSocketEventSubscriber = class {
|
|
|
2063
2327
|
this.broadcast({
|
|
2064
2328
|
event: "chunk",
|
|
2065
2329
|
data: {
|
|
2066
|
-
|
|
2330
|
+
type: payload.type,
|
|
2331
|
+
content: payload.content,
|
|
2067
2332
|
isComplete: payload.isComplete,
|
|
2068
2333
|
sessionId: payload.sessionId
|
|
2069
2334
|
}
|
|
@@ -2105,12 +2370,17 @@ var WebSocketEventSubscriber = class {
|
|
|
2105
2370
|
eventBus.on(
|
|
2106
2371
|
"llmservice:response",
|
|
2107
2372
|
(payload) => {
|
|
2373
|
+
logger.debug(
|
|
2374
|
+
`[websocket-subscriber]: llmservice:response: ${JSON.stringify(payload)}`
|
|
2375
|
+
);
|
|
2108
2376
|
this.broadcast({
|
|
2109
2377
|
event: "response",
|
|
2110
2378
|
data: {
|
|
2111
2379
|
text: payload.content,
|
|
2112
|
-
|
|
2380
|
+
reasoning: payload.reasoning,
|
|
2381
|
+
tokenUsage: payload.tokenUsage,
|
|
2113
2382
|
model: payload.model,
|
|
2383
|
+
router: payload.router,
|
|
2114
2384
|
sessionId: payload.sessionId
|
|
2115
2385
|
}
|
|
2116
2386
|
});
|
|
@@ -2356,10 +2626,10 @@ var WebhookEventSubscriber = class {
|
|
|
2356
2626
|
});
|
|
2357
2627
|
};
|
|
2358
2628
|
if (process.env.NODE_ENV === "test") {
|
|
2359
|
-
const results = await Promise.allSettled(deliveryPromises.map((
|
|
2629
|
+
const results = await Promise.allSettled(deliveryPromises.map((p7) => p7.promise));
|
|
2360
2630
|
handleSettled(results);
|
|
2361
2631
|
} else {
|
|
2362
|
-
Promise.allSettled(deliveryPromises.map((
|
|
2632
|
+
Promise.allSettled(deliveryPromises.map((p7) => p7.promise)).then(handleSettled);
|
|
2363
2633
|
}
|
|
2364
2634
|
}
|
|
2365
2635
|
/**
|
|
@@ -2517,11 +2787,11 @@ async function initializeMcpServer(agent, agentCardData, mcpTransport) {
|
|
|
2517
2787
|
logger.info(
|
|
2518
2788
|
`MCP tool '${toolName}' received message: ${message.substring(0, 100)}${message.length > 100 ? "..." : ""}`
|
|
2519
2789
|
);
|
|
2520
|
-
const
|
|
2790
|
+
const text3 = await agent.run(message);
|
|
2521
2791
|
logger.info(
|
|
2522
|
-
`MCP tool '${toolName}' sending response: ${
|
|
2792
|
+
`MCP tool '${toolName}' sending response: ${text3?.substring(0, 100)}${(text3?.length ?? 0) > 100 ? "..." : ""}`
|
|
2523
2793
|
);
|
|
2524
|
-
return { content: [{ type: "text", text:
|
|
2794
|
+
return { content: [{ type: "text", text: text3 ?? "" }] };
|
|
2525
2795
|
}
|
|
2526
2796
|
);
|
|
2527
2797
|
logger.info(
|
|
@@ -2579,57 +2849,6 @@ async function initializeMcpServerApiEndpoints(app, mcpTransport) {
|
|
|
2579
2849
|
import { stringify as yamlStringify } from "yaml";
|
|
2580
2850
|
import os from "os";
|
|
2581
2851
|
|
|
2582
|
-
// src/app/api/middleware/redactor.ts
|
|
2583
|
-
var SENSITIVE_FIELDS = [
|
|
2584
|
-
"apikey",
|
|
2585
|
-
"api_key",
|
|
2586
|
-
"token",
|
|
2587
|
-
"access_token",
|
|
2588
|
-
"refresh_token",
|
|
2589
|
-
"password",
|
|
2590
|
-
"secret"
|
|
2591
|
-
];
|
|
2592
|
-
var SENSITIVE_PATTERNS = [
|
|
2593
|
-
/\bsk-[A-Za-z0-9]{20,}\b/g,
|
|
2594
|
-
// OpenAI API keys (at least 20 chars after sk-)
|
|
2595
|
-
/\bBearer\s+[A-Za-z0-9\-_.=]+\b/gi,
|
|
2596
|
-
// Bearer tokens
|
|
2597
|
-
/\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b/g,
|
|
2598
|
-
// Emails
|
|
2599
|
-
/\beyJ[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*\.[A-Za-z0-9_-]*/g
|
|
2600
|
-
// JWT tokens
|
|
2601
|
-
];
|
|
2602
|
-
var REDACTED = "[REDACTED]";
|
|
2603
|
-
var REDACTED_CIRCULAR = "[REDACTED_CIRCULAR]";
|
|
2604
|
-
function redactSensitiveData(input, seen = /* @__PURE__ */ new WeakSet()) {
|
|
2605
|
-
if (typeof input === "string") {
|
|
2606
|
-
let result = input;
|
|
2607
|
-
for (const pattern of SENSITIVE_PATTERNS) {
|
|
2608
|
-
result = result.replace(pattern, REDACTED);
|
|
2609
|
-
}
|
|
2610
|
-
return result;
|
|
2611
|
-
}
|
|
2612
|
-
if (Array.isArray(input)) {
|
|
2613
|
-
if (seen.has(input)) return REDACTED_CIRCULAR;
|
|
2614
|
-
seen.add(input);
|
|
2615
|
-
return input.map((item) => redactSensitiveData(item, seen));
|
|
2616
|
-
}
|
|
2617
|
-
if (input && typeof input === "object") {
|
|
2618
|
-
if (seen.has(input)) return REDACTED_CIRCULAR;
|
|
2619
|
-
seen.add(input);
|
|
2620
|
-
const result = {};
|
|
2621
|
-
for (const [key, value] of Object.entries(input)) {
|
|
2622
|
-
if (SENSITIVE_FIELDS.includes(key.toLowerCase())) {
|
|
2623
|
-
result[key] = REDACTED;
|
|
2624
|
-
} else {
|
|
2625
|
-
result[key] = redactSensitiveData(value, seen);
|
|
2626
|
-
}
|
|
2627
|
-
}
|
|
2628
|
-
return result;
|
|
2629
|
-
}
|
|
2630
|
-
return input;
|
|
2631
|
-
}
|
|
2632
|
-
|
|
2633
2852
|
// src/app/api/middleware/expressRedactionMiddleware.ts
|
|
2634
2853
|
function expressRedactionMiddleware(req, res, next) {
|
|
2635
2854
|
const originalJson = res.json.bind(res);
|
|
@@ -2649,6 +2868,136 @@ function expressRedactionMiddleware(req, res, next) {
|
|
|
2649
2868
|
|
|
2650
2869
|
// src/app/api/server.ts
|
|
2651
2870
|
import { z as z2 } from "zod";
|
|
2871
|
+
|
|
2872
|
+
// src/app/api/middleware/errorHandler.ts
|
|
2873
|
+
import { ZodError } from "zod";
|
|
2874
|
+
var mapErrorTypeToStatus = (type) => {
|
|
2875
|
+
switch (type) {
|
|
2876
|
+
case "user" /* USER */:
|
|
2877
|
+
return 400;
|
|
2878
|
+
case "not_found" /* NOT_FOUND */:
|
|
2879
|
+
return 404;
|
|
2880
|
+
case "forbidden" /* FORBIDDEN */:
|
|
2881
|
+
return 403;
|
|
2882
|
+
case "timeout" /* TIMEOUT */:
|
|
2883
|
+
return 408;
|
|
2884
|
+
case "rate_limit" /* RATE_LIMIT */:
|
|
2885
|
+
return 429;
|
|
2886
|
+
case "system" /* SYSTEM */:
|
|
2887
|
+
return 500;
|
|
2888
|
+
case "third_party" /* THIRD_PARTY */:
|
|
2889
|
+
return 502;
|
|
2890
|
+
case "unknown" /* UNKNOWN */:
|
|
2891
|
+
default:
|
|
2892
|
+
return 500;
|
|
2893
|
+
}
|
|
2894
|
+
};
|
|
2895
|
+
var statusForValidation = (issues) => {
|
|
2896
|
+
const firstError = issues.find((i) => i.severity === "error");
|
|
2897
|
+
const type = firstError?.type ?? "user" /* USER */;
|
|
2898
|
+
return mapErrorTypeToStatus(type);
|
|
2899
|
+
};
|
|
2900
|
+
function errorHandler(err, _req, res, _next) {
|
|
2901
|
+
if (err instanceof DextoRuntimeError) {
|
|
2902
|
+
const status = mapErrorTypeToStatus(err.type);
|
|
2903
|
+
res.status(status).json(err.toJSON());
|
|
2904
|
+
return;
|
|
2905
|
+
}
|
|
2906
|
+
if (err instanceof DextoValidationError) {
|
|
2907
|
+
const status = statusForValidation(err.issues);
|
|
2908
|
+
res.status(status).json(err.toJSON());
|
|
2909
|
+
return;
|
|
2910
|
+
}
|
|
2911
|
+
if (err instanceof ZodError) {
|
|
2912
|
+
const issues = zodToIssues(err);
|
|
2913
|
+
const dexErr = new DextoValidationError(issues);
|
|
2914
|
+
const status = statusForValidation(issues);
|
|
2915
|
+
res.status(status).json(dexErr.toJSON());
|
|
2916
|
+
return;
|
|
2917
|
+
}
|
|
2918
|
+
const errorMessage = err instanceof Error ? err.message : String(err);
|
|
2919
|
+
const errorStack = err instanceof Error ? err.stack : void 0;
|
|
2920
|
+
logger.error(`Unhandled error in API middleware: ${errorMessage}`, {
|
|
2921
|
+
error: err,
|
|
2922
|
+
stack: errorStack,
|
|
2923
|
+
type: typeof err
|
|
2924
|
+
});
|
|
2925
|
+
res.status(500).json({
|
|
2926
|
+
code: "internal_error",
|
|
2927
|
+
message: "An unexpected error occurred",
|
|
2928
|
+
scope: "system",
|
|
2929
|
+
type: "system",
|
|
2930
|
+
severity: "error"
|
|
2931
|
+
});
|
|
2932
|
+
}
|
|
2933
|
+
|
|
2934
|
+
// src/app/api/websocket-error-handler.ts
|
|
2935
|
+
import { ZodError as ZodError2 } from "zod";
|
|
2936
|
+
function sendWebSocketError(ws, error) {
|
|
2937
|
+
let errorData;
|
|
2938
|
+
if (error instanceof DextoRuntimeError) {
|
|
2939
|
+
errorData = error.toJSON();
|
|
2940
|
+
} else if (error instanceof DextoValidationError) {
|
|
2941
|
+
errorData = error.toJSON();
|
|
2942
|
+
} else if (error instanceof ZodError2) {
|
|
2943
|
+
const issues = zodToIssues(error);
|
|
2944
|
+
const dexErr = new DextoValidationError(issues);
|
|
2945
|
+
errorData = dexErr.toJSON();
|
|
2946
|
+
} else {
|
|
2947
|
+
const errorObj = toError(error);
|
|
2948
|
+
const errorStack = error instanceof Error ? error.stack : void 0;
|
|
2949
|
+
logger.error(`Unhandled WebSocket error: ${errorObj.message}`, {
|
|
2950
|
+
error,
|
|
2951
|
+
stack: errorStack,
|
|
2952
|
+
type: typeof error
|
|
2953
|
+
});
|
|
2954
|
+
errorData = {
|
|
2955
|
+
code: "internal_error",
|
|
2956
|
+
message: errorObj.message,
|
|
2957
|
+
scope: "agent" /* AGENT */,
|
|
2958
|
+
type: "system" /* SYSTEM */,
|
|
2959
|
+
severity: "error"
|
|
2960
|
+
};
|
|
2961
|
+
}
|
|
2962
|
+
try {
|
|
2963
|
+
ws.send(
|
|
2964
|
+
JSON.stringify({
|
|
2965
|
+
event: "error",
|
|
2966
|
+
data: errorData
|
|
2967
|
+
})
|
|
2968
|
+
);
|
|
2969
|
+
} catch (sendErr) {
|
|
2970
|
+
const msg = sendErr instanceof Error ? sendErr.message : String(sendErr);
|
|
2971
|
+
logger.error(`Failed to send WebSocket error frame: ${msg}`, { errorData });
|
|
2972
|
+
}
|
|
2973
|
+
}
|
|
2974
|
+
function sendWebSocketValidationError(ws, message, context) {
|
|
2975
|
+
const dexErr = new DextoValidationError([
|
|
2976
|
+
{
|
|
2977
|
+
code: "agent_api_validation_error" /* API_VALIDATION_ERROR */,
|
|
2978
|
+
message,
|
|
2979
|
+
scope: "agent" /* AGENT */,
|
|
2980
|
+
type: "user" /* USER */,
|
|
2981
|
+
severity: "error",
|
|
2982
|
+
context
|
|
2983
|
+
}
|
|
2984
|
+
]);
|
|
2985
|
+
const data = dexErr.toJSON();
|
|
2986
|
+
logger.error(`Sending WebSocket validation error: ${message}`, { data });
|
|
2987
|
+
try {
|
|
2988
|
+
ws.send(
|
|
2989
|
+
JSON.stringify({
|
|
2990
|
+
event: "error",
|
|
2991
|
+
data
|
|
2992
|
+
})
|
|
2993
|
+
);
|
|
2994
|
+
} catch (sendErr) {
|
|
2995
|
+
const msg = sendErr instanceof Error ? sendErr.message : String(sendErr);
|
|
2996
|
+
logger.error(`Failed to send WebSocket validation error: ${msg}`, { data });
|
|
2997
|
+
}
|
|
2998
|
+
}
|
|
2999
|
+
|
|
3000
|
+
// src/app/api/server.ts
|
|
2652
3001
|
function sendJsonResponse(res, data, statusCode = 200) {
|
|
2653
3002
|
const pretty = res.req.query.pretty === "true" || res.req.query.pretty === "1";
|
|
2654
3003
|
res.status(statusCode);
|
|
@@ -2659,27 +3008,47 @@ function sendJsonResponse(res, data, statusCode = 200) {
|
|
|
2659
3008
|
res.json(data);
|
|
2660
3009
|
}
|
|
2661
3010
|
}
|
|
2662
|
-
var
|
|
2663
|
-
|
|
2664
|
-
|
|
2665
|
-
|
|
2666
|
-
|
|
2667
|
-
|
|
2668
|
-
|
|
2669
|
-
|
|
2670
|
-
|
|
2671
|
-
|
|
2672
|
-
|
|
2673
|
-
|
|
2674
|
-
|
|
2675
|
-
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
3011
|
+
var MessageRequestSchema = z2.object({
|
|
3012
|
+
message: z2.string().optional(),
|
|
3013
|
+
sessionId: z2.string().optional(),
|
|
3014
|
+
stream: z2.boolean().optional(),
|
|
3015
|
+
imageData: z2.object({
|
|
3016
|
+
base64: z2.string(),
|
|
3017
|
+
mimeType: z2.string()
|
|
3018
|
+
}).optional(),
|
|
3019
|
+
fileData: z2.object({
|
|
3020
|
+
base64: z2.string(),
|
|
3021
|
+
mimeType: z2.string(),
|
|
3022
|
+
filename: z2.string().optional()
|
|
3023
|
+
}).optional()
|
|
3024
|
+
}).refine(
|
|
3025
|
+
(data) => {
|
|
3026
|
+
const msg = (data.message ?? "").trim();
|
|
3027
|
+
return msg.length > 0 || !!data.imageData || !!data.fileData;
|
|
3028
|
+
},
|
|
3029
|
+
{ message: "Must provide either message text, image data, or file data" }
|
|
3030
|
+
);
|
|
3031
|
+
var McpServerRequestSchema = z2.object({
|
|
3032
|
+
name: z2.string().min(1, "Server name is required"),
|
|
3033
|
+
config: McpServerConfigSchema
|
|
3034
|
+
});
|
|
3035
|
+
var WebhookRequestSchema = z2.object({
|
|
3036
|
+
url: z2.string().url("Invalid URL format"),
|
|
3037
|
+
secret: z2.string().optional(),
|
|
3038
|
+
description: z2.string().optional()
|
|
3039
|
+
});
|
|
3040
|
+
var SearchQuerySchema = z2.object({
|
|
3041
|
+
q: z2.string().min(1, "Search query is required"),
|
|
3042
|
+
limit: z2.coerce.number().min(1).max(100).optional(),
|
|
3043
|
+
offset: z2.coerce.number().min(0).optional(),
|
|
3044
|
+
sessionId: z2.string().optional(),
|
|
3045
|
+
role: z2.enum(["user", "assistant", "system", "tool"]).optional()
|
|
3046
|
+
});
|
|
3047
|
+
function parseBody(schema, body) {
|
|
3048
|
+
return schema.parse(body);
|
|
3049
|
+
}
|
|
3050
|
+
function parseQuery(schema, query) {
|
|
3051
|
+
return schema.parse(query);
|
|
2683
3052
|
}
|
|
2684
3053
|
async function initializeApi(agent, agentCardOverride) {
|
|
2685
3054
|
const app = express2();
|
|
@@ -2694,145 +3063,96 @@ async function initializeApi(agent, agentCardOverride) {
|
|
|
2694
3063
|
app.get("/health", (req, res) => {
|
|
2695
3064
|
res.status(200).send("OK");
|
|
2696
3065
|
});
|
|
2697
|
-
app.post("/api/message", express2.json(), async (req, res) => {
|
|
3066
|
+
app.post("/api/message", express2.json(), async (req, res, next) => {
|
|
2698
3067
|
logger.info("Received message via POST /api/message");
|
|
2699
|
-
if (!req.body || !req.body.message) {
|
|
2700
|
-
return res.status(400).send({ error: "Missing message content" });
|
|
2701
|
-
}
|
|
2702
3068
|
try {
|
|
2703
|
-
const sessionId =
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
3069
|
+
const { message, sessionId, stream, imageData, fileData } = parseBody(
|
|
3070
|
+
MessageRequestSchema,
|
|
3071
|
+
req.body
|
|
3072
|
+
);
|
|
3073
|
+
const imageDataInput = imageData ? { image: imageData.base64, mimeType: imageData.mimeType } : void 0;
|
|
3074
|
+
const fileDataInput = fileData ? {
|
|
3075
|
+
data: fileData.base64,
|
|
3076
|
+
mimeType: fileData.mimeType,
|
|
3077
|
+
...fileData.filename && { filename: fileData.filename }
|
|
2710
3078
|
} : void 0;
|
|
2711
3079
|
if (imageDataInput) logger.info("Image data included in message.");
|
|
2712
3080
|
if (fileDataInput) logger.info("File data included in message.");
|
|
2713
3081
|
if (sessionId) logger.info(`Message for session: ${sessionId}`);
|
|
2714
|
-
const
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
},
|
|
2721
|
-
{
|
|
2722
|
-
provider: currentConfig.llm.provider,
|
|
2723
|
-
model: currentConfig.llm.model
|
|
2724
|
-
}
|
|
3082
|
+
const response = await agent.run(
|
|
3083
|
+
message || "",
|
|
3084
|
+
imageDataInput,
|
|
3085
|
+
fileDataInput,
|
|
3086
|
+
sessionId,
|
|
3087
|
+
stream || false
|
|
2725
3088
|
);
|
|
2726
|
-
|
|
2727
|
-
const errorMessages = validation.issues.filter((issue) => issue.severity === "error").map((issue) => issue.message);
|
|
2728
|
-
return res.status(400).send({
|
|
2729
|
-
error: errorMessages.join("; "),
|
|
2730
|
-
provider: currentConfig.llm.provider,
|
|
2731
|
-
model: currentConfig.llm.model,
|
|
2732
|
-
issues: validation.issues
|
|
2733
|
-
});
|
|
2734
|
-
}
|
|
2735
|
-
await agent.run(req.body.message, imageDataInput, fileDataInput, sessionId, stream);
|
|
2736
|
-
return res.status(202).send({ status: "processing", sessionId });
|
|
3089
|
+
return res.status(202).send({ response, sessionId });
|
|
2737
3090
|
} catch (error) {
|
|
2738
|
-
|
|
2739
|
-
logger.error(`Error handling POST /api/message: ${errorMessage}`);
|
|
2740
|
-
return res.status(500).send({ error: "Internal server error" });
|
|
3091
|
+
return next(error);
|
|
2741
3092
|
}
|
|
2742
3093
|
});
|
|
2743
|
-
app.post("/api/message-sync", express2.json(), async (req, res) => {
|
|
3094
|
+
app.post("/api/message-sync", express2.json(), async (req, res, next) => {
|
|
2744
3095
|
logger.info("Received message via POST /api/message-sync");
|
|
2745
|
-
if (!req.body || !req.body.message) {
|
|
2746
|
-
return res.status(400).send({ error: "Missing message content" });
|
|
2747
|
-
}
|
|
2748
3096
|
try {
|
|
2749
|
-
const
|
|
2750
|
-
|
|
2751
|
-
|
|
2752
|
-
|
|
2753
|
-
|
|
3097
|
+
const { message, sessionId, imageData, fileData } = parseBody(
|
|
3098
|
+
MessageRequestSchema,
|
|
3099
|
+
req.body
|
|
3100
|
+
);
|
|
3101
|
+
const imageDataInput = imageData ? { image: imageData.base64, mimeType: imageData.mimeType } : void 0;
|
|
3102
|
+
const fileDataInput = fileData ? {
|
|
3103
|
+
data: fileData.base64,
|
|
3104
|
+
mimeType: fileData.mimeType,
|
|
3105
|
+
...fileData.filename && { filename: fileData.filename }
|
|
2754
3106
|
} : void 0;
|
|
2755
|
-
const sessionId = req.body.sessionId;
|
|
2756
|
-
const stream = req.body.stream === true;
|
|
2757
3107
|
if (imageDataInput) logger.info("Image data included in message.");
|
|
2758
3108
|
if (fileDataInput) logger.info("File data included in message.");
|
|
2759
3109
|
if (sessionId) logger.info(`Message for session: ${sessionId}`);
|
|
2760
|
-
const
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
{
|
|
2768
|
-
provider: currentConfig.llm.provider,
|
|
2769
|
-
model: currentConfig.llm.model
|
|
2770
|
-
}
|
|
3110
|
+
const response = await agent.run(
|
|
3111
|
+
message || "",
|
|
3112
|
+
imageDataInput,
|
|
3113
|
+
fileDataInput,
|
|
3114
|
+
sessionId,
|
|
3115
|
+
false
|
|
3116
|
+
// Force non-streaming for sync endpoint
|
|
2771
3117
|
);
|
|
2772
|
-
|
|
2773
|
-
const errorMessages = validation.issues.filter((issue) => issue.severity === "error").map((issue) => issue.message);
|
|
2774
|
-
return res.status(400).send({
|
|
2775
|
-
error: errorMessages.join("; "),
|
|
2776
|
-
provider: currentConfig.llm.provider,
|
|
2777
|
-
model: currentConfig.llm.model,
|
|
2778
|
-
issues: validation.issues
|
|
2779
|
-
});
|
|
2780
|
-
}
|
|
2781
|
-
await agent.run(req.body.message, imageDataInput, fileDataInput, sessionId, stream);
|
|
2782
|
-
return res.status(202).send({ status: "processing", sessionId });
|
|
3118
|
+
return res.status(200).json({ response, sessionId });
|
|
2783
3119
|
} catch (error) {
|
|
2784
|
-
|
|
2785
|
-
logger.error(`Error handling POST /api/message-sync: ${errorMessage}`);
|
|
2786
|
-
return res.status(500).send({ error: "Internal server error" });
|
|
3120
|
+
return next(error);
|
|
2787
3121
|
}
|
|
2788
3122
|
});
|
|
2789
|
-
app.post("/api/reset", express2.json(), async (req, res) => {
|
|
3123
|
+
app.post("/api/reset", express2.json(), async (req, res, next) => {
|
|
2790
3124
|
logger.info("Received request via POST /api/reset");
|
|
2791
3125
|
try {
|
|
2792
|
-
const sessionId =
|
|
3126
|
+
const { sessionId } = parseBody(
|
|
3127
|
+
z2.object({ sessionId: z2.string().optional() }),
|
|
3128
|
+
req.body
|
|
3129
|
+
);
|
|
2793
3130
|
await agent.resetConversation(sessionId);
|
|
2794
3131
|
return res.status(200).send({ status: "reset initiated", sessionId });
|
|
2795
3132
|
} catch (error) {
|
|
2796
|
-
|
|
2797
|
-
logger.error(`Error handling POST /api/reset: ${errorMessage}`);
|
|
2798
|
-
return res.status(500).send({ error: "Internal server error" });
|
|
3133
|
+
return next(error);
|
|
2799
3134
|
}
|
|
2800
3135
|
});
|
|
2801
|
-
app.post("/api/connect-server", express2.json(), async (req, res) => {
|
|
2802
|
-
const { name, config } = req.body;
|
|
2803
|
-
if (!name || typeof name !== "string" || name.trim() === "") {
|
|
2804
|
-
return res.status(400).send({ error: "Missing or invalid server name" });
|
|
2805
|
-
}
|
|
2806
|
-
if (!config || typeof config !== "object") {
|
|
2807
|
-
return res.status(400).send({ error: "Missing or invalid server config object" });
|
|
2808
|
-
}
|
|
3136
|
+
app.post("/api/connect-server", express2.json(), async (req, res, next) => {
|
|
2809
3137
|
try {
|
|
3138
|
+
const { name, config } = parseBody(McpServerRequestSchema, req.body);
|
|
2810
3139
|
await agent.connectMcpServer(name, config);
|
|
2811
3140
|
logger.info(`Successfully connected to new server '${name}' via API request.`);
|
|
2812
3141
|
return res.status(200).send({ status: "connected", name });
|
|
2813
3142
|
} catch (error) {
|
|
2814
|
-
|
|
2815
|
-
logger.error(`Error handling POST /api/connect-server for '${name}': ${errorMessage}`);
|
|
2816
|
-
return res.status(500).send({
|
|
2817
|
-
error: `Failed to connect to server '${name}': ${errorMessage}`
|
|
2818
|
-
});
|
|
3143
|
+
return next(error);
|
|
2819
3144
|
}
|
|
2820
3145
|
});
|
|
2821
|
-
app.post("/api/mcp/servers", express2.json(), async (req, res) => {
|
|
2822
|
-
const { name, config } = req.body;
|
|
2823
|
-
if (!name || !config) {
|
|
2824
|
-
return res.status(400).json({ error: "Missing name or config" });
|
|
2825
|
-
}
|
|
3146
|
+
app.post("/api/mcp/servers", express2.json(), async (req, res, next) => {
|
|
2826
3147
|
try {
|
|
3148
|
+
const { name, config } = parseBody(McpServerRequestSchema, req.body);
|
|
2827
3149
|
await agent.connectMcpServer(name, config);
|
|
2828
3150
|
return res.status(201).json({ status: "connected", name });
|
|
2829
3151
|
} catch (error) {
|
|
2830
|
-
|
|
2831
|
-
logger.error(`Error connecting MCP server '${name}': ${errorMessage}`);
|
|
2832
|
-
return res.status(500).json({ error: `Failed to connect server: ${errorMessage}` });
|
|
3152
|
+
return next(error);
|
|
2833
3153
|
}
|
|
2834
3154
|
});
|
|
2835
|
-
app.get("/api/mcp/servers", async (req, res) => {
|
|
3155
|
+
app.get("/api/mcp/servers", async (req, res, next) => {
|
|
2836
3156
|
try {
|
|
2837
3157
|
const clientsMap = agent.getMcpClients();
|
|
2838
3158
|
const failedConnections = agent.getMcpFailedConnections();
|
|
@@ -2845,12 +3165,10 @@ async function initializeApi(agent, agentCardOverride) {
|
|
|
2845
3165
|
}
|
|
2846
3166
|
return res.status(200).json({ servers });
|
|
2847
3167
|
} catch (error) {
|
|
2848
|
-
|
|
2849
|
-
logger.error(`Error listing MCP servers: ${errorMessage}`);
|
|
2850
|
-
return res.status(500).json({ error: "Failed to list servers" });
|
|
3168
|
+
return next(error);
|
|
2851
3169
|
}
|
|
2852
3170
|
});
|
|
2853
|
-
app.get("/api/mcp/servers/:serverId/tools", async (req, res) => {
|
|
3171
|
+
app.get("/api/mcp/servers/:serverId/tools", async (req, res, next) => {
|
|
2854
3172
|
const serverId = req.params.serverId;
|
|
2855
3173
|
const client = agent.getMcpClients().get(serverId);
|
|
2856
3174
|
if (!client) {
|
|
@@ -2866,12 +3184,10 @@ async function initializeApi(agent, agentCardOverride) {
|
|
|
2866
3184
|
}));
|
|
2867
3185
|
return res.status(200).json({ tools });
|
|
2868
3186
|
} catch (error) {
|
|
2869
|
-
|
|
2870
|
-
logger.error(`Error fetching tools for server '${serverId}': ${errorMessage}`);
|
|
2871
|
-
return res.status(500).json({ error: "Failed to fetch tools for server" });
|
|
3187
|
+
return next(error);
|
|
2872
3188
|
}
|
|
2873
3189
|
});
|
|
2874
|
-
app.delete("/api/mcp/servers/:serverId", async (req, res) => {
|
|
3190
|
+
app.delete("/api/mcp/servers/:serverId", async (req, res, next) => {
|
|
2875
3191
|
const { serverId } = req.params;
|
|
2876
3192
|
logger.info(`Received request to DELETE /api/mcp/servers/${serverId}`);
|
|
2877
3193
|
try {
|
|
@@ -2883,17 +3199,13 @@ async function initializeApi(agent, agentCardOverride) {
|
|
|
2883
3199
|
await agent.removeMcpServer(serverId);
|
|
2884
3200
|
return res.status(200).json({ status: "disconnected", id: serverId });
|
|
2885
3201
|
} catch (error) {
|
|
2886
|
-
|
|
2887
|
-
logger.error(`Error deleting server '${serverId}': ${errorMessage}`);
|
|
2888
|
-
return res.status(500).json({
|
|
2889
|
-
error: `Failed to delete server '${serverId}': ${errorMessage}`
|
|
2890
|
-
});
|
|
3202
|
+
return next(error);
|
|
2891
3203
|
}
|
|
2892
3204
|
});
|
|
2893
3205
|
app.post(
|
|
2894
3206
|
"/api/mcp/servers/:serverId/tools/:toolName/execute",
|
|
2895
3207
|
express2.json(),
|
|
2896
|
-
async (req, res) => {
|
|
3208
|
+
async (req, res, next) => {
|
|
2897
3209
|
const { serverId, toolName } = req.params;
|
|
2898
3210
|
const client = agent.getMcpClients().get(serverId);
|
|
2899
3211
|
if (!client) {
|
|
@@ -2903,11 +3215,7 @@ async function initializeApi(agent, agentCardOverride) {
|
|
|
2903
3215
|
const rawResult = await agent.executeTool(toolName, req.body);
|
|
2904
3216
|
return res.json({ success: true, data: rawResult });
|
|
2905
3217
|
} catch (error) {
|
|
2906
|
-
|
|
2907
|
-
logger.error(
|
|
2908
|
-
`Error executing tool '${toolName}' on server '${serverId}': ${errorMessage}`
|
|
2909
|
-
);
|
|
2910
|
-
return res.status(500).json({ success: false, error: errorMessage });
|
|
3218
|
+
return next(error);
|
|
2911
3219
|
}
|
|
2912
3220
|
}
|
|
2913
3221
|
);
|
|
@@ -2915,7 +3223,15 @@ async function initializeApi(agent, agentCardOverride) {
|
|
|
2915
3223
|
logger.info("WebSocket client connected.");
|
|
2916
3224
|
ws.on("message", async (messageBuffer) => {
|
|
2917
3225
|
const messageString = messageBuffer.toString();
|
|
2918
|
-
|
|
3226
|
+
try {
|
|
3227
|
+
const parsedMessage = JSON.parse(messageString);
|
|
3228
|
+
const redactedMessage = redactSensitiveData(parsedMessage);
|
|
3229
|
+
logger.debug(`WebSocket received message: ${JSON.stringify(redactedMessage)}`);
|
|
3230
|
+
} catch {
|
|
3231
|
+
const redacted = String(redactSensitiveData(messageString));
|
|
3232
|
+
const truncated = redacted.length > 200 ? `${redacted.substring(0, 200)}... (${redacted.length} total chars)` : redacted;
|
|
3233
|
+
logger.debug(`WebSocket received message: ${truncated}`);
|
|
3234
|
+
}
|
|
2919
3235
|
try {
|
|
2920
3236
|
const data = JSON.parse(messageString);
|
|
2921
3237
|
if (data.type === "toolConfirmationResponse" && data.data) {
|
|
@@ -2923,13 +3239,13 @@ async function initializeApi(agent, agentCardOverride) {
|
|
|
2923
3239
|
return;
|
|
2924
3240
|
} else if (data.type === "message" && (data.content || data.imageData || data.fileData)) {
|
|
2925
3241
|
logger.info(
|
|
2926
|
-
`Processing message from WebSocket: ${data.content.substring(0, 50)}
|
|
3242
|
+
`Processing message from WebSocket: ${data.content ? data.content.substring(0, 50) + "..." : "[image/file only]"}`
|
|
2927
3243
|
);
|
|
2928
3244
|
const imageDataInput = data.imageData ? { image: data.imageData.base64, mimeType: data.imageData.mimeType } : void 0;
|
|
2929
3245
|
const fileDataInput = data.fileData ? {
|
|
2930
3246
|
data: data.fileData.base64,
|
|
2931
3247
|
mimeType: data.fileData.mimeType,
|
|
2932
|
-
filename: data.fileData.filename
|
|
3248
|
+
...data.fileData.filename && { filename: data.fileData.filename }
|
|
2933
3249
|
} : void 0;
|
|
2934
3250
|
const sessionId = data.sessionId;
|
|
2935
3251
|
const stream = data.stream === true;
|
|
@@ -2949,19 +3265,28 @@ async function initializeApi(agent, agentCardOverride) {
|
|
|
2949
3265
|
}
|
|
2950
3266
|
);
|
|
2951
3267
|
if (!validation.ok) {
|
|
2952
|
-
const
|
|
2953
|
-
|
|
2954
|
-
error: errorMessages.join("; "),
|
|
3268
|
+
const redactedIssues = redactSensitiveData(validation.issues);
|
|
3269
|
+
logger.error(`Invalid input for current LLM configuration`, {
|
|
2955
3270
|
provider: currentConfig.llm.provider,
|
|
2956
3271
|
model: currentConfig.llm.model,
|
|
2957
|
-
issues:
|
|
2958
|
-
};
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
3272
|
+
issues: redactedIssues
|
|
3273
|
+
});
|
|
3274
|
+
const hierarchicalError = new DextoValidationError([
|
|
3275
|
+
{
|
|
3276
|
+
code: "agent_api_validation_error" /* API_VALIDATION_ERROR */,
|
|
3277
|
+
message: "Invalid input for current LLM configuration",
|
|
3278
|
+
scope: "agent" /* AGENT */,
|
|
3279
|
+
type: "user" /* USER */,
|
|
3280
|
+
severity: "error",
|
|
3281
|
+
context: {
|
|
3282
|
+
provider: currentConfig.llm.provider,
|
|
3283
|
+
model: currentConfig.llm.model,
|
|
3284
|
+
detailedIssues: validation.issues
|
|
3285
|
+
// Nest the specific validation details
|
|
3286
|
+
}
|
|
3287
|
+
}
|
|
3288
|
+
]);
|
|
3289
|
+
sendWebSocketError(ws, hierarchicalError);
|
|
2965
3290
|
return;
|
|
2966
3291
|
}
|
|
2967
3292
|
await agent.run(data.content, imageDataInput, fileDataInput, sessionId, stream);
|
|
@@ -2973,22 +3298,15 @@ async function initializeApi(agent, agentCardOverride) {
|
|
|
2973
3298
|
await agent.resetConversation(sessionId);
|
|
2974
3299
|
} else {
|
|
2975
3300
|
logger.warn(`Received unknown WebSocket message type: ${data.type}`);
|
|
2976
|
-
ws
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
data: { message: "Unknown message type" }
|
|
2980
|
-
})
|
|
2981
|
-
);
|
|
3301
|
+
sendWebSocketValidationError(ws, "Unknown message type", {
|
|
3302
|
+
messageType: data.type
|
|
3303
|
+
});
|
|
2982
3304
|
}
|
|
2983
3305
|
} catch (error) {
|
|
2984
|
-
|
|
2985
|
-
|
|
2986
|
-
ws.send(
|
|
2987
|
-
JSON.stringify({
|
|
2988
|
-
event: "error",
|
|
2989
|
-
data: { message: "Failed to process message" }
|
|
2990
|
-
})
|
|
3306
|
+
logger.error(
|
|
3307
|
+
`Error processing WebSocket message: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
2991
3308
|
);
|
|
3309
|
+
sendWebSocketError(ws, error);
|
|
2992
3310
|
}
|
|
2993
3311
|
});
|
|
2994
3312
|
ws.on("close", () => {
|
|
@@ -3058,7 +3376,7 @@ async function initializeApi(agent, agentCardOverride) {
|
|
|
3058
3376
|
}
|
|
3059
3377
|
return redactedServers;
|
|
3060
3378
|
}
|
|
3061
|
-
app.get("/api/config.yaml", async (req, res) => {
|
|
3379
|
+
app.get("/api/config.yaml", async (req, res, next) => {
|
|
3062
3380
|
try {
|
|
3063
3381
|
const sessionId = req.query.sessionId;
|
|
3064
3382
|
const config = agent.getEffectiveConfig(sessionId);
|
|
@@ -3074,21 +3392,19 @@ async function initializeApi(agent, agentCardOverride) {
|
|
|
3074
3392
|
res.set("Content-Type", "application/x-yaml");
|
|
3075
3393
|
res.send(yamlStr);
|
|
3076
3394
|
} catch (error) {
|
|
3077
|
-
|
|
3078
|
-
res.status(500).json({ error: "Failed to export configuration" });
|
|
3395
|
+
return next(error);
|
|
3079
3396
|
}
|
|
3080
3397
|
});
|
|
3081
|
-
app.get("/api/llm/current", async (req, res) => {
|
|
3398
|
+
app.get("/api/llm/current", async (req, res, next) => {
|
|
3082
3399
|
try {
|
|
3083
3400
|
const { sessionId } = req.query;
|
|
3084
3401
|
const currentConfig = sessionId ? agent.getEffectiveConfig(sessionId).llm : agent.getCurrentLLMConfig();
|
|
3085
3402
|
res.json({ config: currentConfig });
|
|
3086
3403
|
} catch (error) {
|
|
3087
|
-
|
|
3088
|
-
res.status(500).json({ error: "Failed to get current LLM configuration" });
|
|
3404
|
+
return next(error);
|
|
3089
3405
|
}
|
|
3090
3406
|
});
|
|
3091
|
-
app.get("/api/llm/providers", async (req, res) => {
|
|
3407
|
+
app.get("/api/llm/providers", async (req, res, next) => {
|
|
3092
3408
|
try {
|
|
3093
3409
|
const providers = {};
|
|
3094
3410
|
for (const provider of LLM_PROVIDERS) {
|
|
@@ -3103,67 +3419,50 @@ async function initializeApi(agent, agentCardOverride) {
|
|
|
3103
3419
|
}
|
|
3104
3420
|
res.json({ providers });
|
|
3105
3421
|
} catch (error) {
|
|
3106
|
-
|
|
3107
|
-
res.status(500).json({ error: "Failed to get LLM providers" });
|
|
3422
|
+
return next(error);
|
|
3108
3423
|
}
|
|
3109
3424
|
});
|
|
3110
|
-
app.post("/api/llm/switch", express2.json(), async (req, res) => {
|
|
3111
|
-
const validation = validateBody(LLMSwitchRequestSchema, req.body);
|
|
3112
|
-
if (!validation.success) {
|
|
3113
|
-
return res.status(400).json(validation.response);
|
|
3114
|
-
}
|
|
3115
|
-
const { sessionId, ...llmConfig } = validation.data;
|
|
3425
|
+
app.post("/api/llm/switch", express2.json(), async (req, res, next) => {
|
|
3116
3426
|
try {
|
|
3427
|
+
const body = req.body ?? {};
|
|
3428
|
+
const sessionId = typeof body.sessionId === "string" ? body.sessionId : void 0;
|
|
3429
|
+
const { sessionId: _omit, ...llmCandidate } = body;
|
|
3430
|
+
const llmConfig = LLMUpdatesSchema.parse(llmCandidate);
|
|
3117
3431
|
const config = await agent.switchLLM(llmConfig, sessionId);
|
|
3118
|
-
return res.status(200).json({
|
|
3119
|
-
ok: true,
|
|
3120
|
-
data: config,
|
|
3121
|
-
issues: []
|
|
3122
|
-
});
|
|
3432
|
+
return res.status(200).json({ config, sessionId });
|
|
3123
3433
|
} catch (error) {
|
|
3124
|
-
|
|
3125
|
-
return res.status(400).json({
|
|
3126
|
-
ok: false,
|
|
3127
|
-
issues: error.issues
|
|
3128
|
-
});
|
|
3129
|
-
} else {
|
|
3130
|
-
logger.error("LLM switch failed:", error);
|
|
3131
|
-
return res.status(500).json({
|
|
3132
|
-
ok: false,
|
|
3133
|
-
issues: [
|
|
3134
|
-
{
|
|
3135
|
-
code: "internal_server_error",
|
|
3136
|
-
message: "Internal server error during LLM switch",
|
|
3137
|
-
severity: "error",
|
|
3138
|
-
context: {}
|
|
3139
|
-
}
|
|
3140
|
-
]
|
|
3141
|
-
});
|
|
3142
|
-
}
|
|
3434
|
+
return next(error);
|
|
3143
3435
|
}
|
|
3144
3436
|
});
|
|
3145
|
-
app.get("/api/sessions", async (req, res) => {
|
|
3437
|
+
app.get("/api/sessions", async (req, res, next) => {
|
|
3146
3438
|
try {
|
|
3147
3439
|
const sessionIds = await agent.listSessions();
|
|
3148
3440
|
const sessions = await Promise.all(
|
|
3149
3441
|
sessionIds.map(async (id) => {
|
|
3150
|
-
|
|
3151
|
-
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
3442
|
+
try {
|
|
3443
|
+
const metadata = await agent.getSessionMetadata(id);
|
|
3444
|
+
return {
|
|
3445
|
+
id,
|
|
3446
|
+
createdAt: metadata?.createdAt || null,
|
|
3447
|
+
lastActivity: metadata?.lastActivity || null,
|
|
3448
|
+
messageCount: metadata?.messageCount || 0
|
|
3449
|
+
};
|
|
3450
|
+
} catch (_error) {
|
|
3451
|
+
return {
|
|
3452
|
+
id,
|
|
3453
|
+
createdAt: null,
|
|
3454
|
+
lastActivity: null,
|
|
3455
|
+
messageCount: 0
|
|
3456
|
+
};
|
|
3457
|
+
}
|
|
3157
3458
|
})
|
|
3158
3459
|
);
|
|
3159
3460
|
return res.json({ sessions });
|
|
3160
3461
|
} catch (error) {
|
|
3161
|
-
|
|
3162
|
-
logger.error(`Error listing sessions: ${errorMessage}`);
|
|
3163
|
-
return res.status(500).json({ error: "Failed to list sessions" });
|
|
3462
|
+
return next(error);
|
|
3164
3463
|
}
|
|
3165
3464
|
});
|
|
3166
|
-
app.post("/api/sessions", express2.json(), async (req, res) => {
|
|
3465
|
+
app.post("/api/sessions", express2.json(), async (req, res, next) => {
|
|
3167
3466
|
try {
|
|
3168
3467
|
const { sessionId } = req.body;
|
|
3169
3468
|
const session = await agent.createSession(sessionId);
|
|
@@ -3171,24 +3470,26 @@ async function initializeApi(agent, agentCardOverride) {
|
|
|
3171
3470
|
return res.status(201).json({
|
|
3172
3471
|
session: {
|
|
3173
3472
|
id: session.id,
|
|
3174
|
-
createdAt: metadata?.createdAt ||
|
|
3175
|
-
lastActivity: metadata?.lastActivity ||
|
|
3473
|
+
createdAt: metadata?.createdAt || Date.now(),
|
|
3474
|
+
lastActivity: metadata?.lastActivity || Date.now(),
|
|
3176
3475
|
messageCount: metadata?.messageCount || 0
|
|
3177
3476
|
}
|
|
3178
3477
|
});
|
|
3179
3478
|
} catch (error) {
|
|
3180
|
-
|
|
3181
|
-
|
|
3182
|
-
|
|
3479
|
+
return next(error);
|
|
3480
|
+
}
|
|
3481
|
+
});
|
|
3482
|
+
app.get("/api/sessions/current", async (req, res, next) => {
|
|
3483
|
+
try {
|
|
3484
|
+
const currentSessionId = agent.getCurrentSessionId();
|
|
3485
|
+
return res.json({ currentSessionId });
|
|
3486
|
+
} catch (error) {
|
|
3487
|
+
return next(error);
|
|
3183
3488
|
}
|
|
3184
3489
|
});
|
|
3185
|
-
app.get("/api/sessions/:sessionId", async (req, res) => {
|
|
3490
|
+
app.get("/api/sessions/:sessionId", async (req, res, next) => {
|
|
3186
3491
|
try {
|
|
3187
3492
|
const { sessionId } = req.params;
|
|
3188
|
-
const session = await agent.getSession(sessionId);
|
|
3189
|
-
if (!session) {
|
|
3190
|
-
return res.status(404).json({ error: "Session not found" });
|
|
3191
|
-
}
|
|
3192
3493
|
const metadata = await agent.getSessionMetadata(sessionId);
|
|
3193
3494
|
const history = await agent.getSessionHistory(sessionId);
|
|
3194
3495
|
return res.json({
|
|
@@ -3201,84 +3502,61 @@ async function initializeApi(agent, agentCardOverride) {
|
|
|
3201
3502
|
}
|
|
3202
3503
|
});
|
|
3203
3504
|
} catch (error) {
|
|
3204
|
-
|
|
3205
|
-
logger.error(`Error getting session ${req.params.sessionId}: ${errorMessage}`);
|
|
3206
|
-
return res.status(500).json({ error: "Failed to get session details" });
|
|
3505
|
+
return next(error);
|
|
3207
3506
|
}
|
|
3208
3507
|
});
|
|
3209
|
-
app.get("/api/sessions/:sessionId/history", async (req, res) => {
|
|
3508
|
+
app.get("/api/sessions/:sessionId/history", async (req, res, next) => {
|
|
3210
3509
|
try {
|
|
3211
3510
|
const { sessionId } = req.params;
|
|
3212
|
-
const session = await agent.getSession(sessionId);
|
|
3213
|
-
if (!session) {
|
|
3214
|
-
return res.status(404).json({ error: "Session not found" });
|
|
3215
|
-
}
|
|
3216
3511
|
const history = await agent.getSessionHistory(sessionId);
|
|
3217
3512
|
return res.json({ history });
|
|
3218
3513
|
} catch (error) {
|
|
3219
|
-
|
|
3220
|
-
logger.error(
|
|
3221
|
-
`Error getting session history for ${req.params.sessionId}: ${errorMessage}`
|
|
3222
|
-
);
|
|
3223
|
-
return res.status(500).json({ error: "Failed to get session history" });
|
|
3514
|
+
return next(error);
|
|
3224
3515
|
}
|
|
3225
3516
|
});
|
|
3226
|
-
app.get("/api/search/messages", async (req, res) => {
|
|
3517
|
+
app.get("/api/search/messages", async (req, res, next) => {
|
|
3227
3518
|
try {
|
|
3228
|
-
const
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3519
|
+
const {
|
|
3520
|
+
q: query,
|
|
3521
|
+
limit,
|
|
3522
|
+
offset,
|
|
3523
|
+
sessionId,
|
|
3524
|
+
role
|
|
3525
|
+
} = parseQuery(SearchQuerySchema, req.query);
|
|
3232
3526
|
const options = {
|
|
3233
|
-
limit:
|
|
3234
|
-
offset:
|
|
3527
|
+
limit: limit || 20,
|
|
3528
|
+
offset: offset || 0,
|
|
3529
|
+
...sessionId && { sessionId },
|
|
3530
|
+
...role && { role }
|
|
3235
3531
|
};
|
|
3236
|
-
const sessionId = req.query.sessionId;
|
|
3237
|
-
const role = req.query.role;
|
|
3238
|
-
if (sessionId) {
|
|
3239
|
-
options.sessionId = sessionId;
|
|
3240
|
-
}
|
|
3241
|
-
if (role) {
|
|
3242
|
-
options.role = role;
|
|
3243
|
-
}
|
|
3244
3532
|
const searchResults = await agent.searchMessages(query, options);
|
|
3245
3533
|
return sendJsonResponse(res, searchResults);
|
|
3246
3534
|
} catch (error) {
|
|
3247
|
-
|
|
3248
|
-
logger.error(`Error searching messages: ${errorMessage}`);
|
|
3249
|
-
return res.status(500).json({ error: "Failed to search messages" });
|
|
3535
|
+
return next(error);
|
|
3250
3536
|
}
|
|
3251
3537
|
});
|
|
3252
|
-
app.get("/api/search/sessions", async (req, res) => {
|
|
3538
|
+
app.get("/api/search/sessions", async (req, res, next) => {
|
|
3253
3539
|
try {
|
|
3254
|
-
const query =
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3540
|
+
const { q: query } = parseQuery(
|
|
3541
|
+
z2.object({ q: z2.string().min(1, "Search query is required") }),
|
|
3542
|
+
req.query
|
|
3543
|
+
);
|
|
3258
3544
|
const searchResults = await agent.searchSessions(query);
|
|
3259
3545
|
return sendJsonResponse(res, searchResults);
|
|
3260
3546
|
} catch (error) {
|
|
3261
|
-
|
|
3262
|
-
logger.error(`Error searching sessions: ${errorMessage}`);
|
|
3263
|
-
return res.status(500).json({ error: "Failed to search sessions" });
|
|
3547
|
+
return next(error);
|
|
3264
3548
|
}
|
|
3265
3549
|
});
|
|
3266
|
-
app.delete("/api/sessions/:sessionId", async (req, res) => {
|
|
3550
|
+
app.delete("/api/sessions/:sessionId", async (req, res, next) => {
|
|
3267
3551
|
try {
|
|
3268
3552
|
const { sessionId } = req.params;
|
|
3269
|
-
const session = await agent.getSession(sessionId);
|
|
3270
|
-
if (!session) {
|
|
3271
|
-
return res.status(404).json({ error: "Session not found" });
|
|
3272
|
-
}
|
|
3273
3553
|
await agent.deleteSession(sessionId);
|
|
3274
3554
|
return res.json({ status: "deleted", sessionId });
|
|
3275
3555
|
} catch (error) {
|
|
3276
|
-
|
|
3277
|
-
logger.error(`Error deleting session ${req.params.sessionId}: ${errorMessage}`);
|
|
3278
|
-
return res.status(500).json({ error: "Failed to delete session" });
|
|
3556
|
+
return next(error);
|
|
3279
3557
|
}
|
|
3280
3558
|
});
|
|
3281
|
-
app.post("/api/sessions/:sessionId/load", async (req, res) => {
|
|
3559
|
+
app.post("/api/sessions/:sessionId/load", async (req, res, next) => {
|
|
3282
3560
|
try {
|
|
3283
3561
|
const { sessionId } = req.params;
|
|
3284
3562
|
if (sessionId === "null" || sessionId === "undefined") {
|
|
@@ -3290,10 +3568,6 @@ async function initializeApi(agent, agentCardOverride) {
|
|
|
3290
3568
|
});
|
|
3291
3569
|
return;
|
|
3292
3570
|
}
|
|
3293
|
-
const session = await agent.getSession(sessionId);
|
|
3294
|
-
if (!session) {
|
|
3295
|
-
return res.status(404).json({ error: "Session not found" });
|
|
3296
|
-
}
|
|
3297
3571
|
await agent.loadSession(sessionId);
|
|
3298
3572
|
return res.json({
|
|
3299
3573
|
status: "loaded",
|
|
@@ -3301,35 +3575,15 @@ async function initializeApi(agent, agentCardOverride) {
|
|
|
3301
3575
|
currentSession: agent.getCurrentSessionId()
|
|
3302
3576
|
});
|
|
3303
3577
|
} catch (error) {
|
|
3304
|
-
|
|
3305
|
-
logger.error(`Error loading session ${req.params.sessionId}: ${errorMessage}`);
|
|
3306
|
-
return res.status(500).json({ error: "Failed to load session" });
|
|
3578
|
+
return next(error);
|
|
3307
3579
|
}
|
|
3308
3580
|
});
|
|
3309
|
-
|
|
3581
|
+
const webhookSubscriber = new WebhookEventSubscriber();
|
|
3582
|
+
logger.info("Setting up webhook event subscriptions...");
|
|
3583
|
+
webhookSubscriber.subscribe(agent.agentEventBus);
|
|
3584
|
+
app.post("/api/webhooks", express2.json(), async (req, res, next) => {
|
|
3310
3585
|
try {
|
|
3311
|
-
const
|
|
3312
|
-
return res.json({ currentSessionId });
|
|
3313
|
-
} catch (error) {
|
|
3314
|
-
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
|
3315
|
-
logger.error(`Error getting current session: ${errorMessage}`);
|
|
3316
|
-
return res.status(500).json({ error: "Failed to get current session" });
|
|
3317
|
-
}
|
|
3318
|
-
});
|
|
3319
|
-
const webhookSubscriber = new WebhookEventSubscriber();
|
|
3320
|
-
logger.info("Setting up webhook event subscriptions...");
|
|
3321
|
-
webhookSubscriber.subscribe(agent.agentEventBus);
|
|
3322
|
-
app.post("/api/webhooks", express2.json(), async (req, res) => {
|
|
3323
|
-
try {
|
|
3324
|
-
const { url, secret, description } = req.body;
|
|
3325
|
-
if (!url || typeof url !== "string") {
|
|
3326
|
-
return res.status(400).json({ error: "Invalid or missing webhook URL" });
|
|
3327
|
-
}
|
|
3328
|
-
try {
|
|
3329
|
-
new URL(url);
|
|
3330
|
-
} catch {
|
|
3331
|
-
return res.status(400).json({ error: "Invalid URL format" });
|
|
3332
|
-
}
|
|
3586
|
+
const { url, secret, description } = parseBody(WebhookRequestSchema, req.body);
|
|
3333
3587
|
const webhookId = `wh_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
3334
3588
|
const webhook = {
|
|
3335
3589
|
id: webhookId,
|
|
@@ -3353,12 +3607,10 @@ async function initializeApi(agent, agentCardOverride) {
|
|
|
3353
3607
|
201
|
|
3354
3608
|
);
|
|
3355
3609
|
} catch (error) {
|
|
3356
|
-
|
|
3357
|
-
logger.error(`Error registering webhook: ${errorMessage}`);
|
|
3358
|
-
return res.status(500).json({ error: "Failed to register webhook" });
|
|
3610
|
+
return next(error);
|
|
3359
3611
|
}
|
|
3360
3612
|
});
|
|
3361
|
-
app.get("/api/webhooks", async (req, res) => {
|
|
3613
|
+
app.get("/api/webhooks", async (req, res, next) => {
|
|
3362
3614
|
try {
|
|
3363
3615
|
const webhooks = webhookSubscriber.getWebhooks().map((webhook) => ({
|
|
3364
3616
|
id: webhook.id,
|
|
@@ -3368,12 +3620,10 @@ async function initializeApi(agent, agentCardOverride) {
|
|
|
3368
3620
|
}));
|
|
3369
3621
|
return sendJsonResponse(res, { webhooks });
|
|
3370
3622
|
} catch (error) {
|
|
3371
|
-
|
|
3372
|
-
logger.error(`Error listing webhooks: ${errorMessage}`);
|
|
3373
|
-
return res.status(500).json({ error: "Failed to list webhooks" });
|
|
3623
|
+
return next(error);
|
|
3374
3624
|
}
|
|
3375
3625
|
});
|
|
3376
|
-
app.get("/api/webhooks/:webhookId", async (req, res) => {
|
|
3626
|
+
app.get("/api/webhooks/:webhookId", async (req, res, next) => {
|
|
3377
3627
|
try {
|
|
3378
3628
|
const { webhookId } = req.params;
|
|
3379
3629
|
const webhook = webhookSubscriber.getWebhook(webhookId);
|
|
@@ -3389,12 +3639,10 @@ async function initializeApi(agent, agentCardOverride) {
|
|
|
3389
3639
|
}
|
|
3390
3640
|
});
|
|
3391
3641
|
} catch (error) {
|
|
3392
|
-
|
|
3393
|
-
logger.error(`Error getting webhook ${req.params.webhookId}: ${errorMessage}`);
|
|
3394
|
-
return res.status(500).json({ error: "Failed to get webhook" });
|
|
3642
|
+
return next(error);
|
|
3395
3643
|
}
|
|
3396
3644
|
});
|
|
3397
|
-
app.delete("/api/webhooks/:webhookId", async (req, res) => {
|
|
3645
|
+
app.delete("/api/webhooks/:webhookId", async (req, res, next) => {
|
|
3398
3646
|
try {
|
|
3399
3647
|
const { webhookId } = req.params;
|
|
3400
3648
|
const removed = webhookSubscriber.removeWebhook(webhookId);
|
|
@@ -3404,12 +3652,10 @@ async function initializeApi(agent, agentCardOverride) {
|
|
|
3404
3652
|
logger.info(`Webhook removed: ${webhookId}`);
|
|
3405
3653
|
return res.json({ status: "removed", webhookId });
|
|
3406
3654
|
} catch (error) {
|
|
3407
|
-
|
|
3408
|
-
logger.error(`Error removing webhook ${req.params.webhookId}: ${errorMessage}`);
|
|
3409
|
-
return res.status(500).json({ error: "Failed to remove webhook" });
|
|
3655
|
+
return next(error);
|
|
3410
3656
|
}
|
|
3411
3657
|
});
|
|
3412
|
-
app.post("/api/webhooks/:webhookId/test", async (req, res) => {
|
|
3658
|
+
app.post("/api/webhooks/:webhookId/test", async (req, res, next) => {
|
|
3413
3659
|
try {
|
|
3414
3660
|
const { webhookId } = req.params;
|
|
3415
3661
|
const webhook = webhookSubscriber.getWebhook(webhookId);
|
|
@@ -3428,11 +3674,10 @@ async function initializeApi(agent, agentCardOverride) {
|
|
|
3428
3674
|
}
|
|
3429
3675
|
});
|
|
3430
3676
|
} catch (error) {
|
|
3431
|
-
|
|
3432
|
-
logger.error(`Error testing webhook ${req.params.webhookId}: ${errorMessage}`);
|
|
3433
|
-
return res.status(500).json({ error: "Failed to test webhook" });
|
|
3677
|
+
return next(error);
|
|
3434
3678
|
}
|
|
3435
3679
|
});
|
|
3680
|
+
app.use(errorHandler);
|
|
3436
3681
|
return { app, server, wss, webSubscriber, webhookSubscriber };
|
|
3437
3682
|
}
|
|
3438
3683
|
function startLegacyWebUI(app) {
|
|
@@ -3481,11 +3726,11 @@ async function startApiAndLegacyWebUIServer(agent, port = 3e3, serveLegacyWebUI,
|
|
|
3481
3726
|
}
|
|
3482
3727
|
|
|
3483
3728
|
// src/app/discord/bot.ts
|
|
3484
|
-
import
|
|
3729
|
+
import dotenv2 from "dotenv";
|
|
3485
3730
|
import { Client, GatewayIntentBits, Partials } from "discord.js";
|
|
3486
3731
|
import https from "https";
|
|
3487
3732
|
import http2 from "http";
|
|
3488
|
-
|
|
3733
|
+
dotenv2.config();
|
|
3489
3734
|
var token = process.env.DISCORD_BOT_TOKEN;
|
|
3490
3735
|
var userCooldowns = /* @__PURE__ */ new Map();
|
|
3491
3736
|
var RATE_LIMIT_ENABLED = process.env.DISCORD_RATE_LIMIT_ENABLED?.toLowerCase() !== "false";
|
|
@@ -3646,10 +3891,10 @@ function startDiscordBot(agent) {
|
|
|
3646
3891
|
}
|
|
3647
3892
|
|
|
3648
3893
|
// src/app/telegram/bot.ts
|
|
3649
|
-
import
|
|
3894
|
+
import dotenv3 from "dotenv";
|
|
3650
3895
|
import { Bot, InlineKeyboard } from "grammy";
|
|
3651
3896
|
import https2 from "https";
|
|
3652
|
-
|
|
3897
|
+
dotenv3.config();
|
|
3653
3898
|
var token2 = process.env.TELEGRAM_BOT_TOKEN;
|
|
3654
3899
|
var MAX_CONCURRENT_INLINE_QUERIES = process.env.TELEGRAM_INLINE_QUERY_CONCURRENCY ? Number(process.env.TELEGRAM_INLINE_QUERY_CONCURRENCY) : 5;
|
|
3655
3900
|
var currentInlineQueries = 0;
|
|
@@ -3835,11 +4080,11 @@ function startTelegramBot(agent) {
|
|
|
3835
4080
|
|
|
3836
4081
|
// src/app/cli/utils/options.ts
|
|
3837
4082
|
import { z as z3 } from "zod";
|
|
4083
|
+
import chalk14 from "chalk";
|
|
3838
4084
|
function validateCliOptions(opts) {
|
|
3839
|
-
|
|
3840
|
-
const supportedProviders = getSupportedProviders().map((p3) => p3.toLowerCase());
|
|
4085
|
+
const supportedProviders = getSupportedProviders().map((p7) => p7.toLowerCase());
|
|
3841
4086
|
const cliOptionShape = z3.object({
|
|
3842
|
-
agent: z3.string().
|
|
4087
|
+
agent: z3.string().min(1, "Agent name or path must not be empty").optional(),
|
|
3843
4088
|
strict: z3.boolean().optional().default(false),
|
|
3844
4089
|
verbose: z3.boolean().optional().default(true),
|
|
3845
4090
|
mode: z3.enum(["cli", "web", "server", "discord", "telegram", "mcp"], {
|
|
@@ -3856,7 +4101,8 @@ function validateCliOptions(opts) {
|
|
|
3856
4101
|
),
|
|
3857
4102
|
provider: z3.string().optional(),
|
|
3858
4103
|
model: z3.string().optional(),
|
|
3859
|
-
router: z3.enum(["vercel", "in-built"]).optional()
|
|
4104
|
+
router: z3.enum(["vercel", "in-built"]).optional(),
|
|
4105
|
+
interactive: z3.boolean().optional().default(true).describe("Enable interactive prompts (set to false with --no-interactive)")
|
|
3860
4106
|
});
|
|
3861
4107
|
const cliOptionSchema = cliOptionShape.refine(
|
|
3862
4108
|
(data) => !data.provider || supportedProviders.includes(data.provider.toLowerCase()),
|
|
@@ -3895,28 +4141,387 @@ function validateCliOptions(opts) {
|
|
|
3895
4141
|
webPort: opts.webPort,
|
|
3896
4142
|
provider: opts.provider,
|
|
3897
4143
|
model: opts.model,
|
|
3898
|
-
router: opts.router
|
|
4144
|
+
router: opts.router,
|
|
4145
|
+
interactive: opts.interactive
|
|
3899
4146
|
});
|
|
3900
|
-
logger.debug("Command-line options validated successfully", "green");
|
|
3901
4147
|
}
|
|
3902
4148
|
function handleCliOptionsError(error) {
|
|
3903
4149
|
if (error instanceof z3.ZodError) {
|
|
3904
|
-
|
|
4150
|
+
console.error(chalk14.red("\u274C Invalid command-line options detected:"));
|
|
3905
4151
|
error.errors.forEach((err) => {
|
|
3906
4152
|
const fieldName = err.path.join(".") || "Unknown Option";
|
|
3907
|
-
|
|
4153
|
+
console.error(chalk14.red(` \u2022 Option '${fieldName}': ${err.message}`));
|
|
3908
4154
|
});
|
|
3909
|
-
|
|
3910
|
-
|
|
4155
|
+
console.error(
|
|
4156
|
+
chalk14.dim(
|
|
4157
|
+
"\nPlease check your command-line arguments or run with --help for usage details."
|
|
4158
|
+
)
|
|
3911
4159
|
);
|
|
3912
4160
|
} else {
|
|
3913
|
-
|
|
3914
|
-
|
|
4161
|
+
console.error(
|
|
4162
|
+
chalk14.red(
|
|
4163
|
+
`\u274C Validation error: ${error instanceof Error ? error.message : JSON.stringify(error)}`
|
|
4164
|
+
)
|
|
3915
4165
|
);
|
|
3916
4166
|
}
|
|
3917
4167
|
process.exit(1);
|
|
3918
4168
|
}
|
|
3919
4169
|
|
|
4170
|
+
// src/app/cli/utils/config-validation.ts
|
|
4171
|
+
import chalk17 from "chalk";
|
|
4172
|
+
|
|
4173
|
+
// src/app/cli/utils/api-key-setup.ts
|
|
4174
|
+
import * as p2 from "@clack/prompts";
|
|
4175
|
+
import chalk16 from "chalk";
|
|
4176
|
+
|
|
4177
|
+
// src/app/cli/utils/env-utils.ts
|
|
4178
|
+
async function updateEnvFileWithLLMKeys(envFilePath, llmProvider, llmApiKey) {
|
|
4179
|
+
logger.debug(
|
|
4180
|
+
`updateEnvFileWithLLMKeys: ${JSON.stringify({
|
|
4181
|
+
envFilePath,
|
|
4182
|
+
llmProvider,
|
|
4183
|
+
hasApiKey: Boolean(llmApiKey)
|
|
4184
|
+
})}`
|
|
4185
|
+
);
|
|
4186
|
+
const updates = {};
|
|
4187
|
+
if (llmProvider && llmApiKey) {
|
|
4188
|
+
const envVar = getPrimaryApiKeyEnvVar(llmProvider);
|
|
4189
|
+
updates[envVar] = llmApiKey;
|
|
4190
|
+
}
|
|
4191
|
+
await updateEnvFile(envFilePath, updates);
|
|
4192
|
+
}
|
|
4193
|
+
|
|
4194
|
+
// src/app/cli/utils/provider-setup.ts
|
|
4195
|
+
import * as p from "@clack/prompts";
|
|
4196
|
+
import chalk15 from "chalk";
|
|
4197
|
+
var PROVIDER_OPTIONS = [
|
|
4198
|
+
{
|
|
4199
|
+
value: "google",
|
|
4200
|
+
label: "\u{1F7E2} Google Gemini",
|
|
4201
|
+
hint: "Free tier available - Recommended for beginners"
|
|
4202
|
+
},
|
|
4203
|
+
{
|
|
4204
|
+
value: "groq",
|
|
4205
|
+
label: "\u{1F7E2} Groq",
|
|
4206
|
+
hint: "Free tier available - Very fast responses"
|
|
4207
|
+
},
|
|
4208
|
+
{
|
|
4209
|
+
value: "openai",
|
|
4210
|
+
label: "\u{1F7E1} OpenAI",
|
|
4211
|
+
hint: "Most popular, requires payment"
|
|
4212
|
+
},
|
|
4213
|
+
{
|
|
4214
|
+
value: "anthropic",
|
|
4215
|
+
label: "\u{1F7E1} Anthropic",
|
|
4216
|
+
hint: "High quality models, requires payment"
|
|
4217
|
+
}
|
|
4218
|
+
];
|
|
4219
|
+
async function selectProvider() {
|
|
4220
|
+
const choice = await p.select({
|
|
4221
|
+
message: "Choose your AI provider",
|
|
4222
|
+
options: PROVIDER_OPTIONS
|
|
4223
|
+
});
|
|
4224
|
+
if (p.isCancel(choice)) {
|
|
4225
|
+
p.cancel("Setup cancelled");
|
|
4226
|
+
process.exit(1);
|
|
4227
|
+
}
|
|
4228
|
+
return choice;
|
|
4229
|
+
}
|
|
4230
|
+
function getProviderDisplayName(provider) {
|
|
4231
|
+
switch (provider) {
|
|
4232
|
+
case "google":
|
|
4233
|
+
return "Google Gemini";
|
|
4234
|
+
case "openai":
|
|
4235
|
+
return "OpenAI";
|
|
4236
|
+
case "anthropic":
|
|
4237
|
+
return "Anthropic";
|
|
4238
|
+
case "groq":
|
|
4239
|
+
return "Groq";
|
|
4240
|
+
default:
|
|
4241
|
+
return provider;
|
|
4242
|
+
}
|
|
4243
|
+
}
|
|
4244
|
+
function isValidApiKeyFormat(apiKey, provider) {
|
|
4245
|
+
switch (provider) {
|
|
4246
|
+
case "google":
|
|
4247
|
+
return apiKey.startsWith("AIza") && apiKey.length > 20;
|
|
4248
|
+
case "openai":
|
|
4249
|
+
return apiKey.startsWith("sk-") && apiKey.length > 40;
|
|
4250
|
+
case "anthropic":
|
|
4251
|
+
return apiKey.startsWith("sk-ant-") && apiKey.length > 40;
|
|
4252
|
+
case "groq":
|
|
4253
|
+
return apiKey.startsWith("gsk_") && apiKey.length > 40;
|
|
4254
|
+
default:
|
|
4255
|
+
return apiKey.length > 10;
|
|
4256
|
+
}
|
|
4257
|
+
}
|
|
4258
|
+
function getProviderInstructions(provider) {
|
|
4259
|
+
switch (provider) {
|
|
4260
|
+
case "google":
|
|
4261
|
+
return {
|
|
4262
|
+
title: chalk15.green("Google Gemini - Free API Key"),
|
|
4263
|
+
content: `1. Visit: ${chalk15.cyan("https://aistudio.google.com/apikey")}
|
|
4264
|
+
2. Sign in with your Google account
|
|
4265
|
+
3. Click "Create API Key"
|
|
4266
|
+
4. Copy the key
|
|
4267
|
+
|
|
4268
|
+
${chalk15.dim("\u2728 Free tier included")}`
|
|
4269
|
+
};
|
|
4270
|
+
case "openai":
|
|
4271
|
+
return {
|
|
4272
|
+
title: chalk15.blue("OpenAI API Key"),
|
|
4273
|
+
content: `1. Visit: ${chalk15.cyan("https://platform.openai.com/api-keys")}
|
|
4274
|
+
2. Sign in to your OpenAI account
|
|
4275
|
+
3. Click "Create new secret key"
|
|
4276
|
+
4. Copy the key
|
|
4277
|
+
|
|
4278
|
+
${chalk15.dim("\u{1F4B0} Requires payment")}`
|
|
4279
|
+
};
|
|
4280
|
+
case "anthropic":
|
|
4281
|
+
return {
|
|
4282
|
+
title: chalk15.magenta("Anthropic API Key"),
|
|
4283
|
+
content: `1. Visit: ${chalk15.cyan("https://console.anthropic.com/settings/keys")}
|
|
4284
|
+
2. Sign in to your Anthropic account
|
|
4285
|
+
3. Click "Create Key"
|
|
4286
|
+
4. Copy the key
|
|
4287
|
+
|
|
4288
|
+
${chalk15.dim("\u{1F4B0} Requires payment")}`
|
|
4289
|
+
};
|
|
4290
|
+
case "groq":
|
|
4291
|
+
return {
|
|
4292
|
+
title: chalk15.yellow("Groq API Key"),
|
|
4293
|
+
content: `1. Visit: ${chalk15.cyan("https://console.groq.com/keys")}
|
|
4294
|
+
2. Sign in with your account
|
|
4295
|
+
3. Click "Create API Key"
|
|
4296
|
+
4. Copy the key
|
|
4297
|
+
|
|
4298
|
+
${chalk15.dim("\u{1F193} Free tier included")}`
|
|
4299
|
+
};
|
|
4300
|
+
default:
|
|
4301
|
+
return null;
|
|
4302
|
+
}
|
|
4303
|
+
}
|
|
4304
|
+
|
|
4305
|
+
// src/app/cli/utils/api-key-setup.ts
|
|
4306
|
+
async function interactiveApiKeySetup(provider) {
|
|
4307
|
+
try {
|
|
4308
|
+
p2.intro(chalk16.cyan("\u{1F511} API Key Setup "));
|
|
4309
|
+
const instructions = getProviderInstructions(provider);
|
|
4310
|
+
p2.note(
|
|
4311
|
+
`Your configuration requires a ${getProviderDisplayName(provider)} API key.
|
|
4312
|
+
|
|
4313
|
+
` + (instructions ? instructions.content : "Please get an API key for this provider."),
|
|
4314
|
+
chalk16.bold(`${getProviderDisplayName(provider)} API Key Required`)
|
|
4315
|
+
);
|
|
4316
|
+
const action = await p2.select({
|
|
4317
|
+
message: "What would you like to do?",
|
|
4318
|
+
options: [
|
|
4319
|
+
{
|
|
4320
|
+
value: "setup",
|
|
4321
|
+
label: "Set up an API key now",
|
|
4322
|
+
hint: "Interactive setup (recommended)"
|
|
4323
|
+
},
|
|
4324
|
+
{
|
|
4325
|
+
value: "manual",
|
|
4326
|
+
label: "Set up manually later",
|
|
4327
|
+
hint: "Get instructions for manual setup"
|
|
4328
|
+
},
|
|
4329
|
+
{
|
|
4330
|
+
value: "exit",
|
|
4331
|
+
label: "Exit",
|
|
4332
|
+
hint: "Quit Dexto for now"
|
|
4333
|
+
}
|
|
4334
|
+
]
|
|
4335
|
+
});
|
|
4336
|
+
if (action === "exit") {
|
|
4337
|
+
p2.cancel("Setup cancelled. Run dexto again when you have an API key!");
|
|
4338
|
+
process.exit(0);
|
|
4339
|
+
}
|
|
4340
|
+
if (action === "manual") {
|
|
4341
|
+
showManualSetupInstructions(provider);
|
|
4342
|
+
console.log(chalk16.dim("\n\u{1F44B} Run dexto again once you have set up your API key!"));
|
|
4343
|
+
process.exit(0);
|
|
4344
|
+
}
|
|
4345
|
+
if (p2.isCancel(action)) {
|
|
4346
|
+
p2.cancel("Setup cancelled");
|
|
4347
|
+
process.exit(1);
|
|
4348
|
+
}
|
|
4349
|
+
const apiKey = await p2.password({
|
|
4350
|
+
message: `Enter your ${getProviderDisplayName(provider)} API key`,
|
|
4351
|
+
mask: "*",
|
|
4352
|
+
validate: (value) => {
|
|
4353
|
+
if (!value || value.trim().length === 0) {
|
|
4354
|
+
return "API key is required";
|
|
4355
|
+
}
|
|
4356
|
+
if (!isValidApiKeyFormat(value.trim(), provider)) {
|
|
4357
|
+
return `Invalid ${getProviderDisplayName(provider)} API key format`;
|
|
4358
|
+
}
|
|
4359
|
+
return void 0;
|
|
4360
|
+
}
|
|
4361
|
+
});
|
|
4362
|
+
if (p2.isCancel(apiKey)) {
|
|
4363
|
+
p2.cancel("Setup cancelled");
|
|
4364
|
+
process.exit(0);
|
|
4365
|
+
}
|
|
4366
|
+
const spinner4 = p2.spinner();
|
|
4367
|
+
spinner4.start("Saving API key...");
|
|
4368
|
+
try {
|
|
4369
|
+
const envFilePath = getDextoEnvPath(process.cwd());
|
|
4370
|
+
await updateEnvFileWithLLMKeys(envFilePath, provider, apiKey.trim());
|
|
4371
|
+
spinner4.stop(
|
|
4372
|
+
`\u2728 API key saved successfully for ${getProviderDisplayName(provider)} in ${envFilePath}!`
|
|
4373
|
+
);
|
|
4374
|
+
p2.outro(chalk16.green(`\u2728 API key setup complete!`));
|
|
4375
|
+
await applyLayeredEnvironmentLoading();
|
|
4376
|
+
} catch (error) {
|
|
4377
|
+
spinner4.stop("Failed to save API key");
|
|
4378
|
+
logger.error(`Failed to update .env file: ${error}`);
|
|
4379
|
+
let instructions2;
|
|
4380
|
+
if (getExecutionContext() === "global-cli") {
|
|
4381
|
+
instructions2 = `1. Create ~/.dexto/.env file
|
|
4382
|
+
2. Add this line: ${getPrimaryApiKeyEnvVar(provider)}=your_api_key_here
|
|
4383
|
+
3. Run dexto again
|
|
4384
|
+
|
|
4385
|
+
Alternatively:
|
|
4386
|
+
\u2022 Set environment variable: export ${getPrimaryApiKeyEnvVar(provider)}=your_api_key_here
|
|
4387
|
+
\u2022 Or run: dexto setup`;
|
|
4388
|
+
} else {
|
|
4389
|
+
instructions2 = `1. Create a .env file in your project root
|
|
4390
|
+
2. Add this line: ${getPrimaryApiKeyEnvVar(provider)}=your_api_key_here
|
|
4391
|
+
3. Run dexto again`;
|
|
4392
|
+
}
|
|
4393
|
+
p2.note(
|
|
4394
|
+
`Manual setup required:
|
|
4395
|
+
|
|
4396
|
+
${instructions2}`,
|
|
4397
|
+
chalk16.yellow("Save this API key manually")
|
|
4398
|
+
);
|
|
4399
|
+
console.error(chalk16.red("\n\u274C API key setup required to continue."));
|
|
4400
|
+
process.exit(1);
|
|
4401
|
+
}
|
|
4402
|
+
} catch (error) {
|
|
4403
|
+
if (p2.isCancel(error)) {
|
|
4404
|
+
p2.cancel("Setup cancelled");
|
|
4405
|
+
process.exit(0);
|
|
4406
|
+
}
|
|
4407
|
+
console.error(chalk16.red("\n\u274C API key setup required to continue."));
|
|
4408
|
+
process.exit(1);
|
|
4409
|
+
}
|
|
4410
|
+
}
|
|
4411
|
+
function showManualSetupInstructions(provider) {
|
|
4412
|
+
const envVar = getPrimaryApiKeyEnvVar(provider);
|
|
4413
|
+
const envInstructions = getExecutionContext() === "global-cli" ? [
|
|
4414
|
+
`${chalk16.bold("2. Recommended: Use dexto setup (easiest):")}`,
|
|
4415
|
+
` dexto setup`,
|
|
4416
|
+
``,
|
|
4417
|
+
`${chalk16.bold("Or manually create ~/.dexto/.env:")}`,
|
|
4418
|
+
` mkdir -p ~/.dexto && echo "${envVar}=your_api_key_here" > ~/.dexto/.env`
|
|
4419
|
+
] : [
|
|
4420
|
+
`${chalk16.bold("2. Create a .env file in your project:")}`,
|
|
4421
|
+
` echo "${envVar}=your_api_key_here" > .env`
|
|
4422
|
+
];
|
|
4423
|
+
const instructions = [
|
|
4424
|
+
`${chalk16.bold("1. Get an API key:")}`,
|
|
4425
|
+
` \u2022 ${chalk16.green("Google Gemini (Free)")}: https://aistudio.google.com/apikey`,
|
|
4426
|
+
` \u2022 ${chalk16.blue("OpenAI")}: https://platform.openai.com/api-keys`,
|
|
4427
|
+
` \u2022 ${chalk16.magenta("Anthropic")}: https://console.anthropic.com/keys`,
|
|
4428
|
+
` \u2022 ${chalk16.yellow("Groq (Free)")}: https://console.groq.com/keys`,
|
|
4429
|
+
``,
|
|
4430
|
+
...envInstructions,
|
|
4431
|
+
` # OR for other providers:`,
|
|
4432
|
+
` # OPENAI_API_KEY=your_key_here`,
|
|
4433
|
+
` # ANTHROPIC_API_KEY=your_key_here`,
|
|
4434
|
+
` # GROQ_API_KEY=your_key_here`,
|
|
4435
|
+
``,
|
|
4436
|
+
`${chalk16.bold("3. Run dexto again:")}`,
|
|
4437
|
+
` dexto | npx dexto`,
|
|
4438
|
+
``,
|
|
4439
|
+
`${chalk16.dim("\u{1F4A1} Tip: Start with Google Gemini for a free experience!")}`
|
|
4440
|
+
].join("\n");
|
|
4441
|
+
p2.note(instructions, chalk16.bold("Manual Setup Instructions"));
|
|
4442
|
+
}
|
|
4443
|
+
|
|
4444
|
+
// src/app/cli/utils/config-validation.ts
|
|
4445
|
+
async function validateAgentConfig(config, interactive = false) {
|
|
4446
|
+
const parseResult = AgentConfigSchema.safeParse(config);
|
|
4447
|
+
if (!parseResult.success) {
|
|
4448
|
+
logger.error(`Agent config validation error: ${JSON.stringify(parseResult.error)}`);
|
|
4449
|
+
const apiKeyError = findApiKeyError(parseResult.error, config);
|
|
4450
|
+
if (apiKeyError && interactive) {
|
|
4451
|
+
logger.debug(
|
|
4452
|
+
`API key error found for ${apiKeyError.provider} provider, retriggering interactive setup`
|
|
4453
|
+
);
|
|
4454
|
+
console.log(
|
|
4455
|
+
chalk17.yellow(`
|
|
4456
|
+
\u{1F511} API key required for ${apiKeyError.provider} provider
|
|
4457
|
+
`)
|
|
4458
|
+
);
|
|
4459
|
+
await interactiveApiKeySetup(apiKeyError.provider);
|
|
4460
|
+
return validateAgentConfig(config, interactive);
|
|
4461
|
+
}
|
|
4462
|
+
console.error(chalk17.red("\u274C Configuration Error:"));
|
|
4463
|
+
formatZodError(parseResult.error);
|
|
4464
|
+
process.exit(1);
|
|
4465
|
+
}
|
|
4466
|
+
return parseResult.data;
|
|
4467
|
+
}
|
|
4468
|
+
function findApiKeyError(error, configData) {
|
|
4469
|
+
for (const issue of error.issues) {
|
|
4470
|
+
if (issue.code === "custom" && hasErrorCode(issue.params, "llm_api_key_missing" /* API_KEY_MISSING */)) {
|
|
4471
|
+
const provider = getProviderFromParams(issue.params);
|
|
4472
|
+
if (provider) {
|
|
4473
|
+
return { provider };
|
|
4474
|
+
}
|
|
4475
|
+
}
|
|
4476
|
+
if (issue.path.includes("apiKey") && issue.message.includes("Missing API key")) {
|
|
4477
|
+
const provider = configData.llm?.provider;
|
|
4478
|
+
if (provider) {
|
|
4479
|
+
return { provider };
|
|
4480
|
+
}
|
|
4481
|
+
}
|
|
4482
|
+
}
|
|
4483
|
+
return null;
|
|
4484
|
+
}
|
|
4485
|
+
function hasErrorCode(params, expectedCode) {
|
|
4486
|
+
return typeof params === "object" && params !== null && "code" in params && params.code === expectedCode;
|
|
4487
|
+
}
|
|
4488
|
+
function getProviderFromParams(params) {
|
|
4489
|
+
if (typeof params === "object" && params !== null && "provider" in params && typeof params.provider === "string") {
|
|
4490
|
+
return params.provider;
|
|
4491
|
+
}
|
|
4492
|
+
return null;
|
|
4493
|
+
}
|
|
4494
|
+
function formatZodError(error) {
|
|
4495
|
+
for (const issue of error.issues) {
|
|
4496
|
+
const path9 = issue.path.length > 0 ? issue.path.join(".") : "config";
|
|
4497
|
+
console.error(chalk17.red(` \u2022 ${path9}: ${issue.message}`));
|
|
4498
|
+
}
|
|
4499
|
+
}
|
|
4500
|
+
|
|
4501
|
+
// src/app/config/cli-overrides.ts
|
|
4502
|
+
function applyCLIOverrides(baseConfig, cliOverrides) {
|
|
4503
|
+
if (!cliOverrides || Object.keys(cliOverrides).length === 0) {
|
|
4504
|
+
return baseConfig;
|
|
4505
|
+
}
|
|
4506
|
+
const mergedConfig = JSON.parse(JSON.stringify(baseConfig));
|
|
4507
|
+
if (!mergedConfig.llm) {
|
|
4508
|
+
mergedConfig.llm = {};
|
|
4509
|
+
}
|
|
4510
|
+
if (cliOverrides.provider) {
|
|
4511
|
+
mergedConfig.llm.provider = cliOverrides.provider;
|
|
4512
|
+
}
|
|
4513
|
+
if (cliOverrides.model) {
|
|
4514
|
+
mergedConfig.llm.model = cliOverrides.model;
|
|
4515
|
+
}
|
|
4516
|
+
if (cliOverrides.router) {
|
|
4517
|
+
mergedConfig.llm.router = cliOverrides.router;
|
|
4518
|
+
}
|
|
4519
|
+
if (cliOverrides.apiKey) {
|
|
4520
|
+
mergedConfig.llm.apiKey = cliOverrides.apiKey;
|
|
4521
|
+
}
|
|
4522
|
+
return mergedConfig;
|
|
4523
|
+
}
|
|
4524
|
+
|
|
3920
4525
|
// src/core/utils/port-utils.ts
|
|
3921
4526
|
function getPort(envVar, defaultPort, varName) {
|
|
3922
4527
|
if (envVar === void 0) {
|
|
@@ -3929,18 +4534,145 @@ function getPort(envVar, defaultPort, varName) {
|
|
|
3929
4534
|
return port;
|
|
3930
4535
|
}
|
|
3931
4536
|
|
|
3932
|
-
// src/app/cli/
|
|
3933
|
-
import
|
|
3934
|
-
import
|
|
3935
|
-
import
|
|
3936
|
-
|
|
4537
|
+
// src/app/cli/commands/create-app.ts
|
|
4538
|
+
import fs3 from "fs-extra";
|
|
4539
|
+
import path4 from "path";
|
|
4540
|
+
import chalk18 from "chalk";
|
|
4541
|
+
|
|
4542
|
+
// src/app/cli/utils/execute.ts
|
|
4543
|
+
import { spawn } from "child_process";
|
|
4544
|
+
var DEFAULT_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
4545
|
+
function executeWithTimeout(command, args, options) {
|
|
4546
|
+
return new Promise((resolve, reject) => {
|
|
4547
|
+
const { cwd, timeoutMs: timeout = DEFAULT_TIMEOUT_MS } = options;
|
|
4548
|
+
const child = spawn(command, args, { cwd });
|
|
4549
|
+
let stdout = "";
|
|
4550
|
+
let stderr = "";
|
|
4551
|
+
const timer = setTimeout(() => {
|
|
4552
|
+
logger.error(`Process timed out after ${timeout}ms, killing process`);
|
|
4553
|
+
child.kill();
|
|
4554
|
+
reject(new Error(`Process timed out after ${timeout}ms`));
|
|
4555
|
+
}, timeout);
|
|
4556
|
+
child.stdout.on("data", (data) => {
|
|
4557
|
+
const text3 = data.toString();
|
|
4558
|
+
stdout += text3;
|
|
4559
|
+
logger.debug(text3);
|
|
4560
|
+
});
|
|
4561
|
+
child.stderr.on("data", (data) => {
|
|
4562
|
+
const text3 = data.toString();
|
|
4563
|
+
stderr += text3;
|
|
4564
|
+
});
|
|
4565
|
+
child.on("error", (error) => {
|
|
4566
|
+
clearTimeout(timer);
|
|
4567
|
+
logger.error(`Error spawning process: ${error.message}`);
|
|
4568
|
+
reject(error);
|
|
4569
|
+
});
|
|
4570
|
+
child.on("close", (code) => {
|
|
4571
|
+
clearTimeout(timer);
|
|
4572
|
+
if (code !== 0) {
|
|
4573
|
+
logger.error(`Process exited with code ${code}
|
|
4574
|
+
${stderr}`);
|
|
4575
|
+
reject(new Error(`Process exited with code ${code}`));
|
|
4576
|
+
} else {
|
|
4577
|
+
logger.debug(`${command} ${args.join(" ")} stdout: ${stdout}`);
|
|
4578
|
+
resolve();
|
|
4579
|
+
}
|
|
4580
|
+
});
|
|
4581
|
+
});
|
|
4582
|
+
}
|
|
4583
|
+
|
|
4584
|
+
// src/app/cli/commands/create-app.ts
|
|
4585
|
+
import * as p3 from "@clack/prompts";
|
|
4586
|
+
|
|
4587
|
+
// src/app/cli/utils/package-mgmt.ts
|
|
4588
|
+
import fsExtra from "fs-extra";
|
|
4589
|
+
import path3 from "path";
|
|
4590
|
+
function getPackageManagerInstallCommand(pm) {
|
|
4591
|
+
switch (pm) {
|
|
4592
|
+
case "npm":
|
|
4593
|
+
return "install";
|
|
4594
|
+
case "yarn":
|
|
4595
|
+
return "add";
|
|
4596
|
+
case "pnpm":
|
|
4597
|
+
return "add";
|
|
4598
|
+
case "bun":
|
|
4599
|
+
return "add";
|
|
4600
|
+
default:
|
|
4601
|
+
return "install";
|
|
4602
|
+
}
|
|
4603
|
+
}
|
|
4604
|
+
function getPackageManager() {
|
|
4605
|
+
const projectRoot = findPackageRoot(process.cwd());
|
|
4606
|
+
if (!projectRoot) {
|
|
4607
|
+
return "npm";
|
|
4608
|
+
}
|
|
4609
|
+
if (fsExtra.existsSync(path3.join(projectRoot, "pnpm-lock.yaml"))) {
|
|
4610
|
+
return "pnpm";
|
|
4611
|
+
}
|
|
4612
|
+
if (fsExtra.existsSync(path3.join(projectRoot, "yarn.lock"))) {
|
|
4613
|
+
return "yarn";
|
|
4614
|
+
}
|
|
4615
|
+
if (fsExtra.existsSync(path3.join(projectRoot, "bun.lockb")) || fsExtra.existsSync(path3.join(projectRoot, "bun.lock"))) {
|
|
4616
|
+
return "bun";
|
|
4617
|
+
}
|
|
4618
|
+
return "npm";
|
|
4619
|
+
}
|
|
4620
|
+
async function addScriptsToPackageJson(scripts) {
|
|
4621
|
+
let packageJson;
|
|
4622
|
+
try {
|
|
4623
|
+
packageJson = await fsExtra.readJSON("package.json");
|
|
4624
|
+
} catch (err) {
|
|
4625
|
+
throw new Error(
|
|
4626
|
+
`Failed to read package.json: ${err instanceof Error ? err.message : String(err)}`
|
|
4627
|
+
);
|
|
4628
|
+
}
|
|
4629
|
+
packageJson.scripts = {
|
|
4630
|
+
...packageJson.scripts,
|
|
4631
|
+
...scripts
|
|
4632
|
+
};
|
|
4633
|
+
logger.debug(`Adding scripts to package.json: ${JSON.stringify(scripts, null, 2)}`);
|
|
4634
|
+
try {
|
|
4635
|
+
logger.debug(
|
|
4636
|
+
`Writing to package.json.
|
|
4637
|
+
Contents: ${JSON.stringify(packageJson, null, 2)}`
|
|
4638
|
+
);
|
|
4639
|
+
await fsExtra.writeJSON("package.json", packageJson, { spaces: 4 });
|
|
4640
|
+
} catch (err) {
|
|
4641
|
+
throw new Error(
|
|
4642
|
+
`Failed to write to package.json: ${err instanceof Error ? err.message : String(err)}`
|
|
4643
|
+
);
|
|
4644
|
+
}
|
|
4645
|
+
}
|
|
4646
|
+
async function checkForFileInCurrentDirectory(fileName) {
|
|
4647
|
+
const file = path3.join(process.cwd(), fileName);
|
|
4648
|
+
let isFilePresent = false;
|
|
4649
|
+
try {
|
|
4650
|
+
await fsExtra.readJSON(file);
|
|
4651
|
+
isFilePresent = true;
|
|
4652
|
+
} catch {
|
|
4653
|
+
isFilePresent = false;
|
|
4654
|
+
}
|
|
4655
|
+
if (isFilePresent) {
|
|
4656
|
+
return;
|
|
4657
|
+
}
|
|
4658
|
+
logger.debug(`${fileName} not found in the current directory.`);
|
|
4659
|
+
throw new FileNotFoundError(`${fileName} not found in the current directory.`);
|
|
4660
|
+
}
|
|
4661
|
+
var FileNotFoundError = class extends Error {
|
|
4662
|
+
constructor(message) {
|
|
4663
|
+
super(message);
|
|
4664
|
+
this.name = "FileNotFoundError";
|
|
4665
|
+
}
|
|
4666
|
+
};
|
|
4667
|
+
|
|
4668
|
+
// src/app/cli/commands/create-app.ts
|
|
3937
4669
|
async function createDextoProject(name) {
|
|
3938
4670
|
const nameRegex = /^[a-zA-Z][a-zA-Z0-9-_]*$/;
|
|
3939
4671
|
let projectName;
|
|
3940
4672
|
if (name) {
|
|
3941
4673
|
if (!nameRegex.test(name)) {
|
|
3942
4674
|
console.log(
|
|
3943
|
-
|
|
4675
|
+
chalk18.red(
|
|
3944
4676
|
"Invalid project name. Must start with a letter and contain only letters, numbers, hyphens or underscores."
|
|
3945
4677
|
)
|
|
3946
4678
|
);
|
|
@@ -3950,18 +4682,18 @@ async function createDextoProject(name) {
|
|
|
3950
4682
|
} else {
|
|
3951
4683
|
let input;
|
|
3952
4684
|
do {
|
|
3953
|
-
input = await
|
|
4685
|
+
input = await p3.text({
|
|
3954
4686
|
message: "What do you want to name your Dexto project?",
|
|
3955
4687
|
placeholder: "my-dexto-project",
|
|
3956
4688
|
defaultValue: "my-dexto-project"
|
|
3957
4689
|
});
|
|
3958
|
-
if (
|
|
3959
|
-
|
|
4690
|
+
if (p3.isCancel(input)) {
|
|
4691
|
+
p3.cancel("Project creation cancelled");
|
|
3960
4692
|
process.exit(0);
|
|
3961
4693
|
}
|
|
3962
4694
|
if (!nameRegex.test(input)) {
|
|
3963
4695
|
console.log(
|
|
3964
|
-
|
|
4696
|
+
chalk18.red(
|
|
3965
4697
|
"Invalid project name. Must start with a letter and contain only letters, numbers, hyphens or underscores."
|
|
3966
4698
|
)
|
|
3967
4699
|
);
|
|
@@ -3969,31 +4701,31 @@ async function createDextoProject(name) {
|
|
|
3969
4701
|
} while (!nameRegex.test(input));
|
|
3970
4702
|
projectName = input;
|
|
3971
4703
|
}
|
|
3972
|
-
const
|
|
3973
|
-
const projectPath =
|
|
3974
|
-
|
|
4704
|
+
const spinner4 = p3.spinner();
|
|
4705
|
+
const projectPath = path4.resolve(process.cwd(), projectName);
|
|
4706
|
+
spinner4.start(`Creating dexto project in ${projectPath}...`);
|
|
3975
4707
|
try {
|
|
3976
|
-
await
|
|
4708
|
+
await fs3.mkdir(projectPath);
|
|
3977
4709
|
} catch (error) {
|
|
3978
4710
|
if (error instanceof Error && "code" in error && error.code === "EEXIST") {
|
|
3979
|
-
|
|
4711
|
+
spinner4.stop(
|
|
3980
4712
|
`Directory "${projectName}" already exists. Please choose a different name or delete the existing directory.`
|
|
3981
4713
|
);
|
|
3982
4714
|
process.exit(1);
|
|
3983
4715
|
} else {
|
|
3984
|
-
|
|
4716
|
+
spinner4.stop(`Failed to create project: ${error}`);
|
|
3985
4717
|
throw error;
|
|
3986
4718
|
}
|
|
3987
4719
|
}
|
|
3988
4720
|
process.chdir(projectPath);
|
|
3989
4721
|
await executeWithTimeout("npm", ["init", "-y"], { cwd: projectPath });
|
|
3990
4722
|
await executeWithTimeout("git", ["init"], { cwd: projectPath });
|
|
3991
|
-
await
|
|
3992
|
-
const packageJson = JSON.parse(await
|
|
4723
|
+
await fs3.writeFile(".gitignore", "node_modules\n.env\ndist\n.dexto\n*.log");
|
|
4724
|
+
const packageJson = JSON.parse(await fs3.readFile("package.json", "utf8"));
|
|
3993
4725
|
packageJson.type = "module";
|
|
3994
|
-
await
|
|
3995
|
-
|
|
3996
|
-
|
|
4726
|
+
await fs3.writeFile("package.json", JSON.stringify(packageJson, null, 2));
|
|
4727
|
+
spinner4.stop("Project files created successfully!");
|
|
4728
|
+
spinner4.start("Installing dependencies...");
|
|
3997
4729
|
const packageManager = getPackageManager();
|
|
3998
4730
|
const installCommand = getPackageManagerInstallCommand(packageManager);
|
|
3999
4731
|
await executeWithTimeout(packageManager, [installCommand, "yaml", "dotenv"], {
|
|
@@ -4004,15 +4736,15 @@ async function createDextoProject(name) {
|
|
|
4004
4736
|
[installCommand, "typescript", "tsx", "ts-node", "@types/node", "--save-dev"],
|
|
4005
4737
|
{ cwd: projectPath }
|
|
4006
4738
|
);
|
|
4007
|
-
|
|
4739
|
+
spinner4.stop("Dependencies installed!");
|
|
4008
4740
|
return projectPath;
|
|
4009
4741
|
}
|
|
4010
4742
|
async function addDextoScriptsToPackageJson(directory, projectPath) {
|
|
4011
4743
|
logger.debug(`Adding dexto scripts to package.json in ${projectPath}`);
|
|
4012
4744
|
await addScriptsToPackageJson({
|
|
4013
4745
|
build: "tsc",
|
|
4014
|
-
start: `node dist/${
|
|
4015
|
-
dev: `node --loader ts-node/esm ${
|
|
4746
|
+
start: `node dist/${path4.join("dexto", "dexto-example.js")}`,
|
|
4747
|
+
dev: `node --loader ts-node/esm ${path4.join(directory, "dexto", "dexto-example.ts")}`
|
|
4016
4748
|
});
|
|
4017
4749
|
logger.debug(`Successfully added dexto scripts to package.json in ${projectPath}`);
|
|
4018
4750
|
}
|
|
@@ -4033,42 +4765,806 @@ async function createTsconfigJson(projectPath, directory) {
|
|
|
4033
4765
|
include: [`${directory}/**/*.ts`],
|
|
4034
4766
|
exclude: ["node_modules", "dist", ".dexto"]
|
|
4035
4767
|
};
|
|
4036
|
-
await
|
|
4768
|
+
await fs3.writeJSON(path4.join(projectPath, "tsconfig.json"), tsconfig, { spaces: 4 });
|
|
4037
4769
|
logger.debug(`Successfully created tsconfig.json in ${projectPath}`);
|
|
4038
4770
|
}
|
|
4039
4771
|
async function postCreateDexto(projectPath, directory) {
|
|
4040
4772
|
const nextSteps = [
|
|
4041
|
-
`1. Go to the project directory: ${
|
|
4042
|
-
`2. Run the example: ${
|
|
4043
|
-
`3. Add/update your API key(s) in ${
|
|
4044
|
-
`4. Check out the agent configuration file ${
|
|
4045
|
-
`5. Try out different LLMs and MCP servers in the agent.yml file`,
|
|
4046
|
-
`6.
|
|
4773
|
+
`1. Go to the project directory: ${chalk18.cyan(`cd ${projectPath}`)}`,
|
|
4774
|
+
`2. Run the example: ${chalk18.cyan(`npm run dev`)}`,
|
|
4775
|
+
`3. Add/update your API key(s) in ${chalk18.cyan(".env")}`,
|
|
4776
|
+
`4. Check out the agent configuration file ${chalk18.cyan(path4.join(directory, "dexto", "agents", "default-agent.yml"))}`,
|
|
4777
|
+
`5. Try out different LLMs and MCP servers in the default-agent.yml file`,
|
|
4778
|
+
`6. Run dexto in your project directory to start the interactive CLI with default-agent.yml file`,
|
|
4779
|
+
`7. Read more about Dexto: ${chalk18.cyan("https://docs.dexto.ai")}`
|
|
4047
4780
|
].join("\n");
|
|
4048
|
-
|
|
4781
|
+
p3.note(nextSteps, chalk18.yellow("Next steps:"));
|
|
4049
4782
|
}
|
|
4050
4783
|
|
|
4051
|
-
// src/app/
|
|
4052
|
-
import
|
|
4784
|
+
// src/app/cli/commands/init-app.ts
|
|
4785
|
+
import * as p4 from "@clack/prompts";
|
|
4786
|
+
import chalk19 from "chalk";
|
|
4787
|
+
import fs5 from "node:fs/promises";
|
|
4788
|
+
import fsExtra2 from "fs-extra";
|
|
4789
|
+
import path5 from "node:path";
|
|
4790
|
+
import { createRequire } from "module";
|
|
4791
|
+
|
|
4792
|
+
// src/app/cli/utils/project-utils.ts
|
|
4793
|
+
import fs4 from "node:fs/promises";
|
|
4794
|
+
import { parseDocument } from "yaml";
|
|
4795
|
+
async function updateDextoConfigFile(filepath, llmProvider) {
|
|
4796
|
+
const fileContent = await fs4.readFile(filepath, "utf8");
|
|
4797
|
+
const doc = parseDocument(fileContent);
|
|
4798
|
+
doc.setIn(["llm", "provider"], llmProvider);
|
|
4799
|
+
doc.setIn(["llm", "apiKey"], `$${getPrimaryApiKeyEnvVar(llmProvider)}`);
|
|
4800
|
+
const defaultModel = getDefaultModelForProvider(llmProvider);
|
|
4801
|
+
if (defaultModel) {
|
|
4802
|
+
doc.setIn(["llm", "model"], defaultModel);
|
|
4803
|
+
}
|
|
4804
|
+
await fs4.writeFile(filepath, doc.toString(), "utf8");
|
|
4805
|
+
}
|
|
4806
|
+
|
|
4807
|
+
// src/app/cli/commands/init-app.ts
|
|
4808
|
+
var require2 = createRequire(import.meta.url);
|
|
4809
|
+
async function getUserInputToInitDextoApp() {
|
|
4810
|
+
const answers = await p4.group(
|
|
4811
|
+
{
|
|
4812
|
+
llmProvider: () => p4.select({
|
|
4813
|
+
message: "Choose your AI provider",
|
|
4814
|
+
options: PROVIDER_OPTIONS
|
|
4815
|
+
}),
|
|
4816
|
+
llmApiKey: async ({ results }) => {
|
|
4817
|
+
const llmProvider = results.llmProvider;
|
|
4818
|
+
const selection = await p4.select({
|
|
4819
|
+
message: `Enter your API key for ${getProviderDisplayName(llmProvider)}?`,
|
|
4820
|
+
options: [
|
|
4821
|
+
{ value: "enter", label: "Enter", hint: "recommended" },
|
|
4822
|
+
{ value: "skip", label: "Skip", hint: "" }
|
|
4823
|
+
],
|
|
4824
|
+
initialValue: "enter"
|
|
4825
|
+
});
|
|
4826
|
+
if (p4.isCancel(selection)) {
|
|
4827
|
+
p4.cancel("Dexto initialization cancelled");
|
|
4828
|
+
process.exit(0);
|
|
4829
|
+
}
|
|
4830
|
+
if (selection === "enter") {
|
|
4831
|
+
const apiKey = await p4.password({
|
|
4832
|
+
message: `Enter your ${getProviderDisplayName(llmProvider)} API key`,
|
|
4833
|
+
mask: "*",
|
|
4834
|
+
validate: (value) => {
|
|
4835
|
+
if (!value || value.trim().length === 0) {
|
|
4836
|
+
return "API key is required";
|
|
4837
|
+
}
|
|
4838
|
+
if (!isValidApiKeyFormat(value.trim(), llmProvider)) {
|
|
4839
|
+
return `Invalid ${getProviderDisplayName(llmProvider)} API key format`;
|
|
4840
|
+
}
|
|
4841
|
+
return void 0;
|
|
4842
|
+
}
|
|
4843
|
+
});
|
|
4844
|
+
if (p4.isCancel(apiKey)) {
|
|
4845
|
+
p4.cancel("Dexto initialization cancelled");
|
|
4846
|
+
process.exit(0);
|
|
4847
|
+
}
|
|
4848
|
+
return apiKey;
|
|
4849
|
+
}
|
|
4850
|
+
return "";
|
|
4851
|
+
},
|
|
4852
|
+
directory: () => p4.text({
|
|
4853
|
+
message: "Enter the directory to add the dexto files in",
|
|
4854
|
+
placeholder: "src/",
|
|
4855
|
+
defaultValue: "src/"
|
|
4856
|
+
}),
|
|
4857
|
+
createExampleFile: () => p4.confirm({
|
|
4858
|
+
message: "Create a dexto example file? [Recommended]",
|
|
4859
|
+
initialValue: true
|
|
4860
|
+
})
|
|
4861
|
+
},
|
|
4862
|
+
{
|
|
4863
|
+
onCancel: () => {
|
|
4864
|
+
p4.cancel("Dexto initialization cancelled");
|
|
4865
|
+
process.exit(0);
|
|
4866
|
+
}
|
|
4867
|
+
}
|
|
4868
|
+
);
|
|
4869
|
+
return answers;
|
|
4870
|
+
}
|
|
4871
|
+
async function initDexto(directory, createExampleFile = true, llmProvider, llmApiKey) {
|
|
4872
|
+
const spinner4 = p4.spinner();
|
|
4873
|
+
try {
|
|
4874
|
+
const packageManager = getPackageManager();
|
|
4875
|
+
const installCommand = getPackageManagerInstallCommand(packageManager);
|
|
4876
|
+
spinner4.start("Installing Dexto...");
|
|
4877
|
+
const label = "latest";
|
|
4878
|
+
logger.debug(
|
|
4879
|
+
`Installing Dexto using ${packageManager} with install command: ${installCommand} and label: ${label}`
|
|
4880
|
+
);
|
|
4881
|
+
try {
|
|
4882
|
+
await executeWithTimeout(packageManager, [installCommand, `dexto@${label}`], {
|
|
4883
|
+
cwd: process.cwd()
|
|
4884
|
+
});
|
|
4885
|
+
} catch (installError) {
|
|
4886
|
+
logger.debug(`Install error: ${installError}`);
|
|
4887
|
+
if (packageManager === "pnpm" && "ERR_PNPM_ADDING_TO_ROOT") {
|
|
4888
|
+
spinner4.stop(chalk19.red("Error: Cannot install in pnpm workspace root"));
|
|
4889
|
+
p4.note(
|
|
4890
|
+
'You are initializing dexto in a pnpm workspace root. Go to a specific package in the workspace and run "pnpm add dexto" instead.',
|
|
4891
|
+
chalk19.yellow("Workspace Error")
|
|
4892
|
+
);
|
|
4893
|
+
return { success: false };
|
|
4894
|
+
}
|
|
4895
|
+
throw installError;
|
|
4896
|
+
}
|
|
4897
|
+
spinner4.stop("Dexto installed successfully!");
|
|
4898
|
+
spinner4.start("Creating Dexto files...");
|
|
4899
|
+
const result = await createDextoDirectories(directory);
|
|
4900
|
+
if (!result.ok) {
|
|
4901
|
+
spinner4.stop(
|
|
4902
|
+
chalk19.inverse(
|
|
4903
|
+
`Dexto already initialized in ${path5.join(directory, "dexto")}. Would you like to overwrite it?`
|
|
4904
|
+
)
|
|
4905
|
+
);
|
|
4906
|
+
const overwrite = await p4.confirm({
|
|
4907
|
+
message: "Overwrite Dexto?",
|
|
4908
|
+
initialValue: false
|
|
4909
|
+
});
|
|
4910
|
+
if (p4.isCancel(overwrite) || !overwrite) {
|
|
4911
|
+
p4.cancel("Dexto initialization cancelled");
|
|
4912
|
+
return { success: false };
|
|
4913
|
+
}
|
|
4914
|
+
}
|
|
4915
|
+
logger.debug("Creating dexto config file...");
|
|
4916
|
+
const dextoDir = path5.join(directory, "dexto");
|
|
4917
|
+
const agentsDir = path5.join(dextoDir, "agents");
|
|
4918
|
+
let configPath;
|
|
4919
|
+
try {
|
|
4920
|
+
configPath = await createDextoConfigFile(agentsDir);
|
|
4921
|
+
logger.debug(`Dexto config file created at ${configPath}`);
|
|
4922
|
+
} catch (configError) {
|
|
4923
|
+
spinner4.stop(chalk19.red("Failed to create agent config file"));
|
|
4924
|
+
logger.error(`Config creation error: ${configError}`);
|
|
4925
|
+
throw new Error(
|
|
4926
|
+
`Failed to create default-agent.yml: ${configError instanceof Error ? configError.message : String(configError)}`
|
|
4927
|
+
);
|
|
4928
|
+
}
|
|
4929
|
+
if (llmProvider) {
|
|
4930
|
+
logger.debug(`Updating dexto config file based on llmProvider: ${llmProvider}`);
|
|
4931
|
+
await updateDextoConfigFile(configPath, llmProvider);
|
|
4932
|
+
logger.debug(`Dexto config file updated with llmProvider: ${llmProvider}`);
|
|
4933
|
+
}
|
|
4934
|
+
if (createExampleFile) {
|
|
4935
|
+
logger.debug("Creating dexto example file...");
|
|
4936
|
+
await createDextoExampleFile(dextoDir);
|
|
4937
|
+
logger.debug("Dexto example file created successfully!");
|
|
4938
|
+
}
|
|
4939
|
+
spinner4.start("Updating .env file with dexto env variables...");
|
|
4940
|
+
logger.debug(
|
|
4941
|
+
`Updating .env file with dexto env variables: directory ${directory}, llmProvider: ${llmProvider}, llmApiKey: [REDACTED]`
|
|
4942
|
+
);
|
|
4943
|
+
const envFilePath = path5.join(process.cwd(), ".env");
|
|
4944
|
+
await updateEnvFileWithLLMKeys(envFilePath, llmProvider, llmApiKey);
|
|
4945
|
+
spinner4.stop("Updated .env file with dexto env variables...");
|
|
4946
|
+
return { success: true };
|
|
4947
|
+
} catch (err) {
|
|
4948
|
+
spinner4.stop(chalk19.inverse("An error occurred initializing Dexto project"));
|
|
4949
|
+
logger.debug(`Error: ${err}`);
|
|
4950
|
+
return { success: false };
|
|
4951
|
+
}
|
|
4952
|
+
}
|
|
4953
|
+
async function postInitDexto(directory) {
|
|
4954
|
+
const nextSteps = [
|
|
4955
|
+
`1. Run the example: ${chalk19.cyan(`node --loader ts-node/esm ${path5.join(directory, "dexto", "dexto-example.ts")}`)}`,
|
|
4956
|
+
`2. Add/update your API key(s) in ${chalk19.cyan(".env")}`,
|
|
4957
|
+
`3. Check out the agent configuration file ${chalk19.cyan(path5.join(directory, "dexto", "agents", "default-agent.yml"))}`,
|
|
4958
|
+
`4. Try out different LLMs and MCP servers in the default-agent.yml file`,
|
|
4959
|
+
`5. Read more about Dexto: ${chalk19.cyan("https://github.com/truffle-ai/dexto")}`
|
|
4960
|
+
].join("\n");
|
|
4961
|
+
p4.note(nextSteps, chalk19.yellow("Next steps:"));
|
|
4962
|
+
}
|
|
4963
|
+
async function createDextoDirectories(directory) {
|
|
4964
|
+
const dirPath = path5.join(directory, "dexto");
|
|
4965
|
+
const agentsPath = path5.join(directory, "dexto", "agents");
|
|
4966
|
+
try {
|
|
4967
|
+
await fs5.access(dirPath);
|
|
4968
|
+
return { ok: false };
|
|
4969
|
+
} catch {
|
|
4970
|
+
await fsExtra2.ensureDir(dirPath);
|
|
4971
|
+
await fsExtra2.ensureDir(agentsPath);
|
|
4972
|
+
return { ok: true, dirPath };
|
|
4973
|
+
}
|
|
4974
|
+
}
|
|
4975
|
+
async function createDextoConfigFile(directory) {
|
|
4976
|
+
await fsExtra2.ensureDir(directory);
|
|
4977
|
+
try {
|
|
4978
|
+
const pkgJsonPath = require2.resolve("dexto/package.json");
|
|
4979
|
+
const pkgDir = path5.dirname(pkgJsonPath);
|
|
4980
|
+
logger.debug(`Package directory: ${pkgDir}`);
|
|
4981
|
+
const templateConfigSrc = path5.join(pkgDir, "agents", "agent-template.yml");
|
|
4982
|
+
logger.debug(`Looking for template at: ${templateConfigSrc}`);
|
|
4983
|
+
const templateExists = await fsExtra2.pathExists(templateConfigSrc);
|
|
4984
|
+
if (!templateExists) {
|
|
4985
|
+
throw new Error(
|
|
4986
|
+
`Template file not found at: ${templateConfigSrc}. This indicates a build issue - the template should be included in the package.`
|
|
4987
|
+
);
|
|
4988
|
+
}
|
|
4989
|
+
const destConfigPath = path5.join(directory, "default-agent.yml");
|
|
4990
|
+
logger.debug(`Copying template to: ${destConfigPath}`);
|
|
4991
|
+
await fsExtra2.copy(templateConfigSrc, destConfigPath);
|
|
4992
|
+
logger.debug(`Successfully created config file at: ${destConfigPath}`);
|
|
4993
|
+
return destConfigPath;
|
|
4994
|
+
} catch (error) {
|
|
4995
|
+
logger.error(`Failed to create Dexto config file: ${error}`);
|
|
4996
|
+
throw error;
|
|
4997
|
+
}
|
|
4998
|
+
}
|
|
4999
|
+
async function createDextoExampleFile(directory) {
|
|
5000
|
+
const baseDir = path5.dirname(directory);
|
|
5001
|
+
const configPath = `./${path5.posix.join(baseDir, "dexto/agents/default-agent.yml")}`;
|
|
5002
|
+
const indexTsLines = [
|
|
5003
|
+
"import 'dotenv/config';",
|
|
5004
|
+
"import { DextoAgent, loadAgentConfig } from 'dexto';",
|
|
5005
|
+
"",
|
|
5006
|
+
"console.log('\u{1F680} Starting Dexto Basic Example\\n');",
|
|
5007
|
+
"",
|
|
5008
|
+
"try {",
|
|
5009
|
+
" // Load the agent configuration",
|
|
5010
|
+
` const config = await loadAgentConfig('${configPath}');`,
|
|
5011
|
+
"",
|
|
5012
|
+
" // Create a new DextoAgent instance",
|
|
5013
|
+
" const agent = new DextoAgent(config);",
|
|
5014
|
+
"",
|
|
5015
|
+
" // Start the agent (connects to MCP servers)",
|
|
5016
|
+
" console.log('\u{1F517} Connecting to MCP servers...');",
|
|
5017
|
+
" await agent.start();",
|
|
5018
|
+
" console.log('\u2705 Agent started successfully!\\n');",
|
|
5019
|
+
"",
|
|
5020
|
+
" // Example 1: Simple task",
|
|
5021
|
+
" console.log('\u{1F4CB} Example 1: Simple information request');",
|
|
5022
|
+
" const request1 = 'What tools do you have available?';",
|
|
5023
|
+
" console.log('Request:', request1);",
|
|
5024
|
+
" const response1 = await agent.run(request1);",
|
|
5025
|
+
" console.log('Response:', response1);",
|
|
5026
|
+
" console.log('\\n\u2014\u2014\u2014\u2014\u2014\u2014\\n');",
|
|
5027
|
+
"",
|
|
5028
|
+
" // Example 2: File operation",
|
|
5029
|
+
" console.log('\u{1F4C4} Example 2: File creation');",
|
|
5030
|
+
` const request2 = 'Create a file called test-output.txt with the content "Hello from Dexto!"';`,
|
|
5031
|
+
" console.log('Request:', request2);",
|
|
5032
|
+
" const response2 = await agent.run(request2);",
|
|
5033
|
+
" console.log('Response:', response2);",
|
|
5034
|
+
" console.log('\\n\u2014\u2014\u2014\u2014\u2014\u2014\\n');",
|
|
5035
|
+
"",
|
|
5036
|
+
" // Example 3: Multi-step conversation",
|
|
5037
|
+
" console.log('\u{1F5E3}\uFE0F Example 3: Multi-step conversation');",
|
|
5038
|
+
` const request3a = 'Create a simple HTML file called demo.html with a heading that says "Dexto Demo"';`,
|
|
5039
|
+
" console.log('Request 3a:', request3a);",
|
|
5040
|
+
" const response3a = await agent.run(request3a);",
|
|
5041
|
+
" console.log('Response:', response3a);",
|
|
5042
|
+
" console.log('\\n\\n');",
|
|
5043
|
+
" const request3b = 'Now add a paragraph to that HTML file explaining what Dexto is';",
|
|
5044
|
+
" console.log('Request 3b:', request3b);",
|
|
5045
|
+
" const response3b = await agent.run(request3b);",
|
|
5046
|
+
" console.log('Response:', response3b);",
|
|
5047
|
+
" console.log('\\n\u2014\u2014\u2014\u2014\u2014\u2014\\n');",
|
|
5048
|
+
"",
|
|
5049
|
+
" // Reset conversation (clear context)",
|
|
5050
|
+
" console.log('\u{1F504} Resetting conversation context...');",
|
|
5051
|
+
" agent.resetConversation();",
|
|
5052
|
+
"",
|
|
5053
|
+
" // Example 4: Complex task",
|
|
5054
|
+
" console.log('\u{1F3D7}\uFE0F Example 4: Complex multi-tool task');",
|
|
5055
|
+
" const request4 = ",
|
|
5056
|
+
" 'Create a simple webpage about AI agents with HTML, CSS, and JavaScript. ' +",
|
|
5057
|
+
" 'The page should have a title, some content about what AI agents are, ' +",
|
|
5058
|
+
" 'and a button that shows an alert when clicked.';",
|
|
5059
|
+
" console.log('Request:', request4);",
|
|
5060
|
+
" const response4 = await agent.run(request4);",
|
|
5061
|
+
" console.log('Response:', response4);",
|
|
5062
|
+
" console.log('\\n\u2014\u2014\u2014\u2014\u2014\u2014\\n');",
|
|
5063
|
+
"",
|
|
5064
|
+
" // Stop the agent (disconnect from MCP servers)",
|
|
5065
|
+
" console.log('\\n\u{1F6D1} Stopping agent...');",
|
|
5066
|
+
" await agent.stop();",
|
|
5067
|
+
" console.log('\u2705 Agent stopped successfully!');",
|
|
5068
|
+
"",
|
|
5069
|
+
"} catch (error) {",
|
|
5070
|
+
" console.error('\u274C Error:', error);",
|
|
5071
|
+
"}",
|
|
5072
|
+
"",
|
|
5073
|
+
"console.log('\\n\u{1F4D6} Read Dexto documentation to understand more about using Dexto: https://docs.dexto.ai');"
|
|
5074
|
+
];
|
|
5075
|
+
const indexTsContent = indexTsLines.join("\n");
|
|
5076
|
+
const outputPath = path5.join(directory, "dexto-example.ts");
|
|
5077
|
+
logger.debug(`Creating example file with config path: ${configPath}`);
|
|
5078
|
+
logger.debug(`Base directory: ${baseDir}, Output path: ${outputPath}`);
|
|
5079
|
+
logger.debug(`Generated file content:
|
|
5080
|
+
${indexTsContent}`);
|
|
5081
|
+
await fs5.writeFile(outputPath, indexTsContent);
|
|
5082
|
+
return outputPath;
|
|
5083
|
+
}
|
|
5084
|
+
|
|
5085
|
+
// src/app/cli/commands/setup.ts
|
|
5086
|
+
import chalk20 from "chalk";
|
|
5087
|
+
import { z as z4 } from "zod";
|
|
5088
|
+
|
|
5089
|
+
// src/app/cli/utils/setup-utils.ts
|
|
5090
|
+
function isFirstTimeUser() {
|
|
5091
|
+
return !globalPreferencesExist();
|
|
5092
|
+
}
|
|
5093
|
+
async function requiresSetup() {
|
|
5094
|
+
if (getExecutionContext() !== "global-cli") {
|
|
5095
|
+
return false;
|
|
5096
|
+
}
|
|
5097
|
+
if (isFirstTimeUser()) {
|
|
5098
|
+
return true;
|
|
5099
|
+
}
|
|
5100
|
+
try {
|
|
5101
|
+
const preferences = await loadGlobalPreferences();
|
|
5102
|
+
if (!preferences.setup.completed) {
|
|
5103
|
+
return true;
|
|
5104
|
+
}
|
|
5105
|
+
if (!preferences.defaults.defaultAgent) {
|
|
5106
|
+
return true;
|
|
5107
|
+
}
|
|
5108
|
+
return false;
|
|
5109
|
+
} catch (_error) {
|
|
5110
|
+
return true;
|
|
5111
|
+
}
|
|
5112
|
+
}
|
|
5113
|
+
|
|
5114
|
+
// src/app/cli/commands/setup.ts
|
|
5115
|
+
import * as p5 from "@clack/prompts";
|
|
5116
|
+
var SetupCommandSchema = z4.object({
|
|
5117
|
+
provider: z4.enum(LLM_PROVIDERS).optional().describe("AI provider identifier to use for LLM calls"),
|
|
5118
|
+
model: z4.string().min(1, "Model name cannot be empty").optional().describe("Preferred model name for the selected provider"),
|
|
5119
|
+
defaultAgent: z4.string().min(1, "Default agent name cannot be empty").default("default-agent").describe("Registry agent id to use when none is specified"),
|
|
5120
|
+
interactive: z4.boolean().default(true).describe("Enable interactive prompts"),
|
|
5121
|
+
force: z4.boolean().default(false).describe("Overwrite existing setup when already configured")
|
|
5122
|
+
}).strict().superRefine((data, ctx) => {
|
|
5123
|
+
if (data.provider && data.model) {
|
|
5124
|
+
if (!isValidProviderModel(data.provider, data.model)) {
|
|
5125
|
+
const supportedModels = getSupportedModels(data.provider);
|
|
5126
|
+
ctx.addIssue({
|
|
5127
|
+
code: z4.ZodIssueCode.custom,
|
|
5128
|
+
path: ["model"],
|
|
5129
|
+
message: `Model '${data.model}' is not supported by provider '${data.provider}'. Supported models: ${supportedModels.join(", ")}`
|
|
5130
|
+
});
|
|
5131
|
+
}
|
|
5132
|
+
}
|
|
5133
|
+
});
|
|
5134
|
+
function validateSetupCommand(options) {
|
|
5135
|
+
const validated = SetupCommandSchema.parse(options);
|
|
5136
|
+
if (!validated.interactive && !validated.provider) {
|
|
5137
|
+
throw new Error("Provider required in non-interactive mode. Use --provider option.");
|
|
5138
|
+
}
|
|
5139
|
+
return validated;
|
|
5140
|
+
}
|
|
5141
|
+
async function handleSetupCommand(options) {
|
|
5142
|
+
const validated = validateSetupCommand(options);
|
|
5143
|
+
logger.debug(`Validated setup command options: ${JSON.stringify(validated, null, 2)}`);
|
|
5144
|
+
const needsSetup = await requiresSetup();
|
|
5145
|
+
if (!needsSetup) {
|
|
5146
|
+
if (!validated.interactive) {
|
|
5147
|
+
if (!validated.force) {
|
|
5148
|
+
console.error(chalk20.red("\u274C Setup is already complete."));
|
|
5149
|
+
console.error(
|
|
5150
|
+
chalk20.dim(
|
|
5151
|
+
" Use --force to overwrite existing setup, or run in interactive mode for confirmation."
|
|
5152
|
+
)
|
|
5153
|
+
);
|
|
5154
|
+
process.exit(1);
|
|
5155
|
+
}
|
|
5156
|
+
logger.warn("Overwriting existing setup due to --force flag");
|
|
5157
|
+
} else {
|
|
5158
|
+
p5.intro(chalk20.yellow("\u26A0\uFE0F Setup Already Complete"));
|
|
5159
|
+
p5.note(
|
|
5160
|
+
"Dexto is already set up and configured.\nRe-running setup will overwrite your current preferences.",
|
|
5161
|
+
"Current Setup Detected"
|
|
5162
|
+
);
|
|
5163
|
+
const shouldContinue = await p5.confirm({
|
|
5164
|
+
message: "Do you want to continue and overwrite your current setup?",
|
|
5165
|
+
initialValue: false
|
|
5166
|
+
});
|
|
5167
|
+
if (p5.isCancel(shouldContinue) || !shouldContinue) {
|
|
5168
|
+
p5.cancel("Setup cancelled. Your existing configuration remains unchanged.");
|
|
5169
|
+
process.exit(0);
|
|
5170
|
+
}
|
|
5171
|
+
p5.log.warn("Proceeding with setup override...");
|
|
5172
|
+
}
|
|
5173
|
+
}
|
|
5174
|
+
console.log(chalk20.cyan("\n\u{1F5FF} Setting up Dexto...\n"));
|
|
5175
|
+
let provider = validated.provider;
|
|
5176
|
+
if (!provider) {
|
|
5177
|
+
provider = await selectProvider();
|
|
5178
|
+
}
|
|
5179
|
+
const model = validated.model || getDefaultModelForProvider(provider);
|
|
5180
|
+
if (!model) {
|
|
5181
|
+
throw new Error(`Provider '${provider}' requires a specific model. Use --model option.`);
|
|
5182
|
+
}
|
|
5183
|
+
const apiKeyVar = getPrimaryApiKeyEnvVar(provider);
|
|
5184
|
+
const defaultAgent = validated.defaultAgent;
|
|
5185
|
+
const preferences = createInitialPreferences(provider, model, apiKeyVar, defaultAgent);
|
|
5186
|
+
await saveGlobalPreferences(preferences);
|
|
5187
|
+
if (validated.interactive) {
|
|
5188
|
+
const existingApiKey = resolveApiKeyForProvider(provider);
|
|
5189
|
+
if (existingApiKey) {
|
|
5190
|
+
p5.outro(chalk20.green(`\u2705 API key for ${provider} already configured`));
|
|
5191
|
+
} else {
|
|
5192
|
+
p5.outro(chalk20.cyan(`API key not found for ${provider}, starting api key setup...`));
|
|
5193
|
+
await interactiveApiKeySetup(provider);
|
|
5194
|
+
}
|
|
5195
|
+
}
|
|
5196
|
+
console.log(chalk20.green("\n\u2728 Setup complete! Dexto is ready to use.\n"));
|
|
5197
|
+
}
|
|
5198
|
+
|
|
5199
|
+
// src/app/cli/commands/install.ts
|
|
4053
5200
|
import { existsSync } from "fs";
|
|
4054
|
-
import
|
|
5201
|
+
import path6 from "path";
|
|
5202
|
+
import { z as z5 } from "zod";
|
|
5203
|
+
var InstallCommandSchema = z5.object({
|
|
5204
|
+
agents: z5.array(z5.string().min(1, "Agent name cannot be empty")),
|
|
5205
|
+
all: z5.boolean().default(false),
|
|
5206
|
+
injectPreferences: z5.boolean().default(true),
|
|
5207
|
+
force: z5.boolean().default(false)
|
|
5208
|
+
}).strict();
|
|
5209
|
+
function validateInstallCommand(agents, options) {
|
|
5210
|
+
const registry = getAgentRegistry();
|
|
5211
|
+
const validated = InstallCommandSchema.parse({
|
|
5212
|
+
...options,
|
|
5213
|
+
agents
|
|
5214
|
+
});
|
|
5215
|
+
const availableAgents = registry.getAvailableAgents();
|
|
5216
|
+
if (!validated.all && validated.agents.length === 0) {
|
|
5217
|
+
throw new Error(
|
|
5218
|
+
`No agents specified. Use agent names or --all flag. Available agents: ${Object.keys(availableAgents).join(", ")}`
|
|
5219
|
+
);
|
|
5220
|
+
}
|
|
5221
|
+
if (!validated.all) {
|
|
5222
|
+
const invalidAgents = validated.agents.filter((agent) => !registry.hasAgent(agent));
|
|
5223
|
+
if (invalidAgents.length > 0) {
|
|
5224
|
+
throw new Error(
|
|
5225
|
+
`Unknown agents: ${invalidAgents.join(", ")}. Available agents: ${Object.keys(availableAgents).join(", ")}`
|
|
5226
|
+
);
|
|
5227
|
+
}
|
|
5228
|
+
}
|
|
5229
|
+
return validated;
|
|
5230
|
+
}
|
|
5231
|
+
async function handleInstallCommand(agents, options) {
|
|
5232
|
+
const validated = validateInstallCommand(agents, options);
|
|
5233
|
+
const registry = getAgentRegistry();
|
|
5234
|
+
let agentsToInstall;
|
|
5235
|
+
if (validated.all) {
|
|
5236
|
+
agentsToInstall = Object.keys(registry.getAvailableAgents());
|
|
5237
|
+
console.log(`\u{1F4CB} Installing all ${agentsToInstall.length} available agents...`);
|
|
5238
|
+
} else {
|
|
5239
|
+
agentsToInstall = validated.agents;
|
|
5240
|
+
}
|
|
5241
|
+
console.log(`\u{1F680} Installing ${agentsToInstall.length} agents...`);
|
|
5242
|
+
let successCount = 0;
|
|
5243
|
+
let errorCount = 0;
|
|
5244
|
+
const errors = [];
|
|
5245
|
+
for (const agentName of agentsToInstall) {
|
|
5246
|
+
try {
|
|
5247
|
+
console.log(`
|
|
5248
|
+
\u{1F4E6} Installing ${agentName}...`);
|
|
5249
|
+
const globalAgentsDir = getDextoGlobalPath("agents");
|
|
5250
|
+
const installedPath = path6.join(globalAgentsDir, agentName);
|
|
5251
|
+
if (existsSync(installedPath) && !validated.force) {
|
|
5252
|
+
console.log(`\u23ED\uFE0F ${agentName} already installed (use --force to reinstall)`);
|
|
5253
|
+
successCount++;
|
|
5254
|
+
continue;
|
|
5255
|
+
}
|
|
5256
|
+
await registry.installAgent(agentName, validated.injectPreferences);
|
|
5257
|
+
successCount++;
|
|
5258
|
+
console.log(`\u2705 ${agentName} installed successfully`);
|
|
5259
|
+
} catch (error) {
|
|
5260
|
+
errorCount++;
|
|
5261
|
+
const errorMsg = `Failed to install ${agentName}: ${error instanceof Error ? error.message : String(error)}`;
|
|
5262
|
+
errors.push(errorMsg);
|
|
5263
|
+
console.error(`\u274C ${errorMsg}`);
|
|
5264
|
+
}
|
|
5265
|
+
}
|
|
5266
|
+
if (agentsToInstall.length === 1) {
|
|
5267
|
+
if (errorCount > 0) {
|
|
5268
|
+
throw new Error(errors[0]);
|
|
5269
|
+
}
|
|
5270
|
+
return;
|
|
5271
|
+
}
|
|
5272
|
+
console.log(`
|
|
5273
|
+
\u{1F4CA} Installation Summary:`);
|
|
5274
|
+
console.log(`\u2705 Successfully installed: ${successCount}`);
|
|
5275
|
+
if (errorCount > 0) {
|
|
5276
|
+
console.log(`\u274C Failed to install: ${errorCount}`);
|
|
5277
|
+
errors.forEach((error) => console.log(` \u2022 ${error}`));
|
|
5278
|
+
}
|
|
5279
|
+
if (errorCount > 0 && successCount === 0) {
|
|
5280
|
+
throw new Error("All installations failed");
|
|
5281
|
+
} else if (errorCount > 0) {
|
|
5282
|
+
console.log(`\u26A0\uFE0F Some installations failed, but ${successCount} succeeded.`);
|
|
5283
|
+
} else {
|
|
5284
|
+
console.log(`\u{1F389} All agents installed successfully!`);
|
|
5285
|
+
}
|
|
5286
|
+
}
|
|
5287
|
+
|
|
5288
|
+
// src/app/cli/commands/uninstall.ts
|
|
5289
|
+
import { z as z6 } from "zod";
|
|
5290
|
+
var UninstallCommandSchema = z6.object({
|
|
5291
|
+
agents: z6.array(z6.string().min(1, "Agent name cannot be empty")),
|
|
5292
|
+
all: z6.boolean().default(false),
|
|
5293
|
+
force: z6.boolean().default(false)
|
|
5294
|
+
}).strict();
|
|
5295
|
+
async function validateUninstallCommand(agents, options) {
|
|
5296
|
+
const validated = UninstallCommandSchema.parse({
|
|
5297
|
+
...options,
|
|
5298
|
+
agents
|
|
5299
|
+
});
|
|
5300
|
+
const registry = getAgentRegistry();
|
|
5301
|
+
const installedAgents = await registry.getInstalledAgents();
|
|
5302
|
+
if (installedAgents.length === 0) {
|
|
5303
|
+
throw new Error("No agents are currently installed.");
|
|
5304
|
+
}
|
|
5305
|
+
if (!validated.all && validated.agents.length === 0) {
|
|
5306
|
+
throw new Error(
|
|
5307
|
+
`No agents specified. Use agent names or --all flag. Installed agents: ${installedAgents.join(", ")}`
|
|
5308
|
+
);
|
|
5309
|
+
}
|
|
5310
|
+
return validated;
|
|
5311
|
+
}
|
|
5312
|
+
async function handleUninstallCommand(agents, options) {
|
|
5313
|
+
const validated = await validateUninstallCommand(agents, options);
|
|
5314
|
+
const registry = getAgentRegistry();
|
|
5315
|
+
const installedAgents = await registry.getInstalledAgents();
|
|
5316
|
+
if (installedAgents.length === 0) {
|
|
5317
|
+
console.log("\u{1F4CB} No agents are currently installed.");
|
|
5318
|
+
return;
|
|
5319
|
+
}
|
|
5320
|
+
let agentsToUninstall;
|
|
5321
|
+
if (validated.all) {
|
|
5322
|
+
agentsToUninstall = installedAgents;
|
|
5323
|
+
console.log(`\u{1F4CB} Uninstalling all ${agentsToUninstall.length} installed agents...`);
|
|
5324
|
+
} else {
|
|
5325
|
+
agentsToUninstall = validated.agents;
|
|
5326
|
+
const notInstalled = agentsToUninstall.filter((agent) => !installedAgents.includes(agent));
|
|
5327
|
+
if (notInstalled.length > 0) {
|
|
5328
|
+
throw new Error(
|
|
5329
|
+
`Agents not installed: ${notInstalled.join(", ")}. Installed agents: ${installedAgents.join(", ")}`
|
|
5330
|
+
);
|
|
5331
|
+
}
|
|
5332
|
+
}
|
|
5333
|
+
console.log(`\u{1F5D1}\uFE0F Uninstalling ${agentsToUninstall.length} agents...`);
|
|
5334
|
+
let successCount = 0;
|
|
5335
|
+
let errorCount = 0;
|
|
5336
|
+
const errors = [];
|
|
5337
|
+
for (const agentName of agentsToUninstall) {
|
|
5338
|
+
try {
|
|
5339
|
+
console.log(`
|
|
5340
|
+
\u{1F5D1}\uFE0F Uninstalling ${agentName}...`);
|
|
5341
|
+
await registry.uninstallAgent(agentName, validated.force);
|
|
5342
|
+
successCount++;
|
|
5343
|
+
console.log(`\u2705 ${agentName} uninstalled successfully`);
|
|
5344
|
+
} catch (error) {
|
|
5345
|
+
errorCount++;
|
|
5346
|
+
const errorMsg = `Failed to uninstall ${agentName}: ${error instanceof Error ? error.message : String(error)}`;
|
|
5347
|
+
errors.push(errorMsg);
|
|
5348
|
+
console.error(`\u274C ${errorMsg}`);
|
|
5349
|
+
}
|
|
5350
|
+
}
|
|
5351
|
+
if (agentsToUninstall.length === 1) {
|
|
5352
|
+
if (errorCount > 0) {
|
|
5353
|
+
throw new Error(errors[0]);
|
|
5354
|
+
}
|
|
5355
|
+
return;
|
|
5356
|
+
}
|
|
5357
|
+
console.log(`
|
|
5358
|
+
\u{1F4CA} Uninstallation Summary:`);
|
|
5359
|
+
console.log(`\u2705 Successfully uninstalled: ${successCount}`);
|
|
5360
|
+
if (errorCount > 0) {
|
|
5361
|
+
console.log(`\u274C Failed to uninstall: ${errorCount}`);
|
|
5362
|
+
errors.forEach((error) => console.log(` \u2022 ${error}`));
|
|
5363
|
+
}
|
|
5364
|
+
if (errorCount > 0 && successCount === 0) {
|
|
5365
|
+
throw new Error("All uninstallations failed");
|
|
5366
|
+
} else if (errorCount > 0) {
|
|
5367
|
+
console.log(`\u26A0\uFE0F Some uninstallations failed, but ${successCount} succeeded.`);
|
|
5368
|
+
} else {
|
|
5369
|
+
console.log(`\u{1F389} All agents uninstalled successfully!`);
|
|
5370
|
+
}
|
|
5371
|
+
}
|
|
5372
|
+
|
|
5373
|
+
// src/app/cli/commands/list-agents.ts
|
|
5374
|
+
import { existsSync as existsSync2 } from "fs";
|
|
5375
|
+
import { promises as fs6 } from "fs";
|
|
5376
|
+
import path7 from "path";
|
|
5377
|
+
import chalk21 from "chalk";
|
|
5378
|
+
import { z as z7 } from "zod";
|
|
5379
|
+
var ListAgentsCommandSchema = z7.object({
|
|
5380
|
+
verbose: z7.boolean().default(false),
|
|
5381
|
+
installed: z7.boolean().default(false),
|
|
5382
|
+
available: z7.boolean().default(false)
|
|
5383
|
+
}).strict();
|
|
5384
|
+
async function getInstalledAgents() {
|
|
5385
|
+
const globalAgentsDir = getDextoGlobalPath("agents");
|
|
5386
|
+
if (!existsSync2(globalAgentsDir)) {
|
|
5387
|
+
return [];
|
|
5388
|
+
}
|
|
5389
|
+
const registry = getAgentRegistry();
|
|
5390
|
+
const installedAgents = [];
|
|
5391
|
+
try {
|
|
5392
|
+
const entries = await fs6.readdir(globalAgentsDir, { withFileTypes: true });
|
|
5393
|
+
for (const entry of entries) {
|
|
5394
|
+
if (entry.isDirectory() || entry.name.endsWith(".yml")) {
|
|
5395
|
+
const agentName = entry.isDirectory() ? entry.name : path7.basename(entry.name, ".yml");
|
|
5396
|
+
const agentPath = path7.join(globalAgentsDir, entry.name);
|
|
5397
|
+
try {
|
|
5398
|
+
const mainConfigPath = entry.isDirectory() ? registry.resolveMainConfig(agentPath, agentName) : agentPath;
|
|
5399
|
+
const stats = await fs6.stat(agentPath);
|
|
5400
|
+
let llmProvider;
|
|
5401
|
+
let llmModel;
|
|
5402
|
+
if (existsSync2(mainConfigPath)) {
|
|
5403
|
+
try {
|
|
5404
|
+
const configContent = await fs6.readFile(mainConfigPath, "utf-8");
|
|
5405
|
+
const configMatch = configContent.match(/provider:\s*([^\n\r]+)/);
|
|
5406
|
+
const modelMatch = configContent.match(/model:\s*([^\n\r]+)/);
|
|
5407
|
+
llmProvider = configMatch?.[1]?.trim();
|
|
5408
|
+
llmModel = modelMatch?.[1]?.trim();
|
|
5409
|
+
} catch (_error) {
|
|
5410
|
+
}
|
|
5411
|
+
}
|
|
5412
|
+
const registryData = registry.getAvailableAgents()[agentName];
|
|
5413
|
+
const description = registryData?.description || "Custom agent";
|
|
5414
|
+
const agentInfo = {
|
|
5415
|
+
name: agentName,
|
|
5416
|
+
description,
|
|
5417
|
+
path: mainConfigPath,
|
|
5418
|
+
installedAt: stats.birthtime || stats.mtime
|
|
5419
|
+
};
|
|
5420
|
+
if (llmProvider) agentInfo.llmProvider = llmProvider;
|
|
5421
|
+
if (llmModel) agentInfo.llmModel = llmModel;
|
|
5422
|
+
installedAgents.push(agentInfo);
|
|
5423
|
+
} catch (error) {
|
|
5424
|
+
console.warn(`Warning: Could not process agent '${agentName}': ${error}`);
|
|
5425
|
+
}
|
|
5426
|
+
}
|
|
5427
|
+
}
|
|
5428
|
+
} catch (_error) {
|
|
5429
|
+
return [];
|
|
5430
|
+
}
|
|
5431
|
+
return installedAgents.sort((a, b) => a.name.localeCompare(b.name));
|
|
5432
|
+
}
|
|
5433
|
+
function getAvailableAgents() {
|
|
5434
|
+
const registry = getAgentRegistry();
|
|
5435
|
+
const availableAgents = registry.getAvailableAgents();
|
|
5436
|
+
return Object.entries(availableAgents).map(([name, data]) => ({
|
|
5437
|
+
name,
|
|
5438
|
+
description: data.description,
|
|
5439
|
+
author: data.author,
|
|
5440
|
+
tags: data.tags
|
|
5441
|
+
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
5442
|
+
}
|
|
5443
|
+
async function handleListAgentsCommand(options) {
|
|
5444
|
+
const validated = ListAgentsCommandSchema.parse(options);
|
|
5445
|
+
console.log(chalk21.cyan("\n\u{1F4CB} Dexto Agents\n"));
|
|
5446
|
+
let globalLLM;
|
|
5447
|
+
if (globalPreferencesExist()) {
|
|
5448
|
+
try {
|
|
5449
|
+
const preferences = await loadGlobalPreferences();
|
|
5450
|
+
globalLLM = `${preferences.llm.provider}/${preferences.llm.model}`;
|
|
5451
|
+
} catch {
|
|
5452
|
+
}
|
|
5453
|
+
}
|
|
5454
|
+
const installedAgents = await getInstalledAgents();
|
|
5455
|
+
const availableAgents = getAvailableAgents();
|
|
5456
|
+
const showInstalled = !validated.available || validated.installed;
|
|
5457
|
+
const showAvailable = !validated.installed || validated.available;
|
|
5458
|
+
if (showInstalled && installedAgents.length > 0) {
|
|
5459
|
+
console.log(chalk21.green("\u2705 Installed Agents:"));
|
|
5460
|
+
for (const agent of installedAgents) {
|
|
5461
|
+
const llmInfo = agent.llmProvider && agent.llmModel ? `${agent.llmProvider}/${agent.llmModel}` : globalLLM || "Unknown LLM";
|
|
5462
|
+
const llmDisplay = chalk21.gray(`(${llmInfo})`);
|
|
5463
|
+
if (validated.verbose) {
|
|
5464
|
+
console.log(` ${chalk21.bold(agent.name)} ${llmDisplay}`);
|
|
5465
|
+
console.log(` ${chalk21.gray(agent.description)}`);
|
|
5466
|
+
console.log(` ${chalk21.gray("Path:")} ${agent.path}`);
|
|
5467
|
+
if (agent.installedAt) {
|
|
5468
|
+
console.log(
|
|
5469
|
+
` ${chalk21.gray("Installed:")} ${agent.installedAt.toLocaleDateString()}`
|
|
5470
|
+
);
|
|
5471
|
+
}
|
|
5472
|
+
console.log();
|
|
5473
|
+
} else {
|
|
5474
|
+
console.log(` \u2022 ${chalk21.bold(agent.name)} ${llmDisplay} - ${agent.description}`);
|
|
5475
|
+
}
|
|
5476
|
+
}
|
|
5477
|
+
console.log();
|
|
5478
|
+
} else if (showInstalled) {
|
|
5479
|
+
console.log(chalk21.yellow("\u{1F4E6} No agents installed yet."));
|
|
5480
|
+
console.log(
|
|
5481
|
+
chalk21.gray(" Use `dexto install <agent-name>` to install agents from the registry.\n")
|
|
5482
|
+
);
|
|
5483
|
+
}
|
|
5484
|
+
if (showAvailable) {
|
|
5485
|
+
const availableNotInstalled = availableAgents.filter(
|
|
5486
|
+
(available) => !installedAgents.some((installed) => installed.name === available.name)
|
|
5487
|
+
);
|
|
5488
|
+
if (availableNotInstalled.length > 0) {
|
|
5489
|
+
console.log(chalk21.blue("\u{1F4CB} Available to Install:"));
|
|
5490
|
+
for (const agent of availableNotInstalled) {
|
|
5491
|
+
if (validated.verbose) {
|
|
5492
|
+
console.log(` ${chalk21.bold(agent.name)}`);
|
|
5493
|
+
console.log(` ${chalk21.gray(agent.description)}`);
|
|
5494
|
+
console.log(` ${chalk21.gray("Author:")} ${agent.author}`);
|
|
5495
|
+
console.log(` ${chalk21.gray("Tags:")} ${agent.tags.join(", ")}`);
|
|
5496
|
+
console.log();
|
|
5497
|
+
} else {
|
|
5498
|
+
console.log(` \u2022 ${chalk21.bold(agent.name)} - ${agent.description}`);
|
|
5499
|
+
}
|
|
5500
|
+
}
|
|
5501
|
+
console.log();
|
|
5502
|
+
}
|
|
5503
|
+
}
|
|
5504
|
+
const totalInstalled = installedAgents.length;
|
|
5505
|
+
const availableToInstall = availableAgents.filter(
|
|
5506
|
+
(a) => !installedAgents.some((i) => i.name === a.name)
|
|
5507
|
+
).length;
|
|
5508
|
+
if (!validated.verbose) {
|
|
5509
|
+
console.log(
|
|
5510
|
+
chalk21.gray(
|
|
5511
|
+
`\u{1F4CA} Summary: ${totalInstalled} installed, ${availableToInstall} available to install`
|
|
5512
|
+
)
|
|
5513
|
+
);
|
|
5514
|
+
if (availableToInstall > 0) {
|
|
5515
|
+
console.log(
|
|
5516
|
+
chalk21.gray(` Use \`dexto install <agent-name>\` to install more agents.`)
|
|
5517
|
+
);
|
|
5518
|
+
}
|
|
5519
|
+
console.log(chalk21.gray(` Use \`dexto list-agents --verbose\` for detailed information.`));
|
|
5520
|
+
}
|
|
5521
|
+
console.log();
|
|
5522
|
+
}
|
|
5523
|
+
|
|
5524
|
+
// src/app/cli/commands/which.ts
|
|
5525
|
+
import chalk22 from "chalk";
|
|
5526
|
+
import { z as z8 } from "zod";
|
|
5527
|
+
var WhichCommandSchema = z8.object({
|
|
5528
|
+
agentName: z8.string().min(1, "Agent name cannot be empty")
|
|
5529
|
+
}).strict();
|
|
5530
|
+
async function handleWhichCommand(agentName) {
|
|
5531
|
+
const validated = WhichCommandSchema.parse({ agentName });
|
|
5532
|
+
const registry = getAgentRegistry();
|
|
5533
|
+
const availableAgents = Object.keys(registry.getAvailableAgents());
|
|
5534
|
+
try {
|
|
5535
|
+
const resolvedPath = await resolveAgentPath(validated.agentName, false, false);
|
|
5536
|
+
console.log(resolvedPath);
|
|
5537
|
+
} catch (error) {
|
|
5538
|
+
console.error(
|
|
5539
|
+
chalk22.red(
|
|
5540
|
+
`\u274C dexto which command failed: ${error instanceof Error ? error.message : String(error)}. Available agents: ${availableAgents.join(", ")}`
|
|
5541
|
+
)
|
|
5542
|
+
);
|
|
5543
|
+
process.exit(1);
|
|
5544
|
+
}
|
|
5545
|
+
}
|
|
5546
|
+
|
|
5547
|
+
// src/app/web.ts
|
|
5548
|
+
import { spawn as spawn2 } from "child_process";
|
|
5549
|
+
import { existsSync as existsSync3 } from "fs";
|
|
5550
|
+
import path8 from "path";
|
|
4055
5551
|
import { fileURLToPath } from "url";
|
|
4056
5552
|
async function startNextJsWebServer(apiUrl, frontPort = 3e3, frontUrl = `http://localhost:${frontPort}`) {
|
|
4057
|
-
const scriptDir =
|
|
5553
|
+
const scriptDir = path8.dirname(fileURLToPath(import.meta.url));
|
|
4058
5554
|
logger.debug(`Script directory for web mode: ${scriptDir}`);
|
|
4059
|
-
let webuiPath =
|
|
4060
|
-
if (!
|
|
4061
|
-
const srcPath =
|
|
4062
|
-
if (
|
|
5555
|
+
let webuiPath = path8.resolve(scriptDir, "webui");
|
|
5556
|
+
if (!existsSync3(webuiPath)) {
|
|
5557
|
+
const srcPath = path8.resolve(scriptDir, "..", "..", "src", "app", "webui");
|
|
5558
|
+
if (existsSync3(srcPath)) {
|
|
4063
5559
|
return startDevServer(apiUrl, frontPort, frontUrl, srcPath);
|
|
4064
5560
|
} else {
|
|
4065
5561
|
logger.warn("Could not locate webui directory. Web UI may not be available.");
|
|
4066
5562
|
return false;
|
|
4067
5563
|
}
|
|
4068
5564
|
}
|
|
4069
|
-
const standaloneServerPath =
|
|
4070
|
-
const serverScriptPath =
|
|
4071
|
-
if (!
|
|
5565
|
+
const standaloneServerPath = path8.join(webuiPath, ".next", "standalone", "server.js");
|
|
5566
|
+
const serverScriptPath = path8.join(webuiPath, "server.js");
|
|
5567
|
+
if (!existsSync3(standaloneServerPath) && !existsSync3(serverScriptPath)) {
|
|
4072
5568
|
logger.warn(
|
|
4073
5569
|
"Built WebUI not found. This may indicate the package was not built correctly.",
|
|
4074
5570
|
null,
|
|
@@ -4088,8 +5584,8 @@ async function startNextJsWebServer(apiUrl, frontPort = 3e3, frontUrl = `http://
|
|
|
4088
5584
|
}
|
|
4089
5585
|
})();
|
|
4090
5586
|
logger.info(`Starting Next.js production server on ${frontUrl}`, null, "cyanBright");
|
|
4091
|
-
const serverToUse =
|
|
4092
|
-
const nextProc =
|
|
5587
|
+
const serverToUse = existsSync3(serverScriptPath) ? serverScriptPath : standaloneServerPath;
|
|
5588
|
+
const nextProc = spawn2("node", [serverToUse], {
|
|
4093
5589
|
cwd: webuiPath,
|
|
4094
5590
|
stdio: ["inherit", "pipe", "inherit"],
|
|
4095
5591
|
env: {
|
|
@@ -4162,11 +5658,11 @@ async function startDevServer(apiUrl, frontPort, frontUrl, webuiPath) {
|
|
|
4162
5658
|
return "3001";
|
|
4163
5659
|
}
|
|
4164
5660
|
})();
|
|
4165
|
-
const nodeModulesPath =
|
|
4166
|
-
const needsInstall = !
|
|
5661
|
+
const nodeModulesPath = path8.join(webuiPath, "node_modules");
|
|
5662
|
+
const needsInstall = !existsSync3(nodeModulesPath);
|
|
4167
5663
|
if (needsInstall) {
|
|
4168
5664
|
logger.info("Installing Next.js dependencies...", null, "cyanBright");
|
|
4169
|
-
const installProc =
|
|
5665
|
+
const installProc = spawn2("npm", ["install"], {
|
|
4170
5666
|
cwd: webuiPath,
|
|
4171
5667
|
stdio: "inherit"
|
|
4172
5668
|
});
|
|
@@ -4180,7 +5676,7 @@ async function startDevServer(apiUrl, frontPort, frontUrl, webuiPath) {
|
|
|
4180
5676
|
}
|
|
4181
5677
|
}
|
|
4182
5678
|
logger.info(`Starting Next.js dev server on ${frontUrl}`, null, "cyanBright");
|
|
4183
|
-
const nextProc =
|
|
5679
|
+
const nextProc = spawn2("npm", ["run", "dev", "--", "--port", String(frontPort)], {
|
|
4184
5680
|
cwd: webuiPath,
|
|
4185
5681
|
stdio: ["inherit", "pipe", "inherit"],
|
|
4186
5682
|
env: {
|
|
@@ -4322,16 +5818,19 @@ async function initializeMcpToolAggregationServer(serverConfigs, mcpTransport, s
|
|
|
4322
5818
|
}
|
|
4323
5819
|
|
|
4324
5820
|
// src/app/index.ts
|
|
4325
|
-
|
|
5821
|
+
await applyLayeredEnvironmentLoading();
|
|
4326
5822
|
var program = new Command();
|
|
4327
|
-
program.name("dexto").description("AI-powered CLI and WebUI for interacting with MCP servers").version(package_default.version, "-v, --version", "output the current version").option("-a, --agent <path>", "
|
|
5823
|
+
program.name("dexto").description("AI-powered CLI and WebUI for interacting with MCP servers").version(package_default.version, "-v, --version", "output the current version").option("-a, --agent <name|path>", "Agent name or path to agent config file").option(
|
|
5824
|
+
"-p, --prompt <text>",
|
|
5825
|
+
"One-shot prompt text. Alternatively provide a single quoted string as positional argument."
|
|
5826
|
+
).option("-s, --strict", "Require all server connections to succeed").option("--no-verbose", "Disable verbose output").option("--no-interactive", "Disable interactive prompts and API key setup").option("-m, --model <model>", "Specify the LLM model to use").option("-r, --router <router>", "Specify the LLM router to use (vercel or in-built)").option("--new-session [sessionId]", "Start with a new session (optionally specify session ID)").option(
|
|
4328
5827
|
"--mode <mode>",
|
|
4329
5828
|
"The application in which dexto should talk to you - cli | web | server | discord | telegram | mcp",
|
|
4330
5829
|
"cli"
|
|
4331
|
-
).option("--web-port <port>", "optional port for the web UI", "3000");
|
|
5830
|
+
).option("--web-port <port>", "optional port for the web UI", "3000").option("--no-auto-install", "Disable automatic installation of missing agents from registry").enablePositionalOptions();
|
|
4332
5831
|
program.command("create-app").description("Scaffold a new Dexto Typescript app").action(async () => {
|
|
4333
5832
|
try {
|
|
4334
|
-
|
|
5833
|
+
p6.intro(chalk23.inverse("Dexto Create App"));
|
|
4335
5834
|
const appPath = await createDextoProject();
|
|
4336
5835
|
const userInput = await getUserInputToInitDextoApp();
|
|
4337
5836
|
process.chdir(appPath);
|
|
@@ -4343,7 +5842,7 @@ program.command("create-app").description("Scaffold a new Dexto Typescript app")
|
|
|
4343
5842
|
userInput.llmProvider,
|
|
4344
5843
|
userInput.llmApiKey
|
|
4345
5844
|
);
|
|
4346
|
-
|
|
5845
|
+
p6.outro(chalk23.greenBright("Dexto app created and initialized successfully!"));
|
|
4347
5846
|
await postCreateDexto(appPath, userInput.directory);
|
|
4348
5847
|
process.exit(0);
|
|
4349
5848
|
} catch (err) {
|
|
@@ -4355,7 +5854,7 @@ program.command("init-app").description("Initialize an existing Typescript app w
|
|
|
4355
5854
|
try {
|
|
4356
5855
|
await checkForFileInCurrentDirectory("package.json");
|
|
4357
5856
|
await checkForFileInCurrentDirectory("tsconfig.json");
|
|
4358
|
-
|
|
5857
|
+
p6.intro(chalk23.inverse("Dexto Init App"));
|
|
4359
5858
|
const userInput = await getUserInputToInitDextoApp();
|
|
4360
5859
|
await initDexto(
|
|
4361
5860
|
userInput.directory,
|
|
@@ -4363,7 +5862,7 @@ program.command("init-app").description("Initialize an existing Typescript app w
|
|
|
4363
5862
|
userInput.llmProvider,
|
|
4364
5863
|
userInput.llmApiKey
|
|
4365
5864
|
);
|
|
4366
|
-
|
|
5865
|
+
p6.outro(chalk23.greenBright("Dexto app initialized successfully!"));
|
|
4367
5866
|
await postInitDexto(userInput.directory);
|
|
4368
5867
|
process.exit(0);
|
|
4369
5868
|
} catch (err) {
|
|
@@ -4375,6 +5874,53 @@ program.command("init-app").description("Initialize an existing Typescript app w
|
|
|
4375
5874
|
process.exit(1);
|
|
4376
5875
|
}
|
|
4377
5876
|
});
|
|
5877
|
+
program.command("setup").description("Configure global Dexto preferences").option("--provider <provider>", "LLM provider (openai, anthropic, google, groq)").option("--model <model>", "Model name (uses provider default if not specified)").option("--default-agent <agent>", "Default agent name (default: default-agent)").option("--no-interactive", "Skip interactive prompts and API key setup").option("--force", "Overwrite existing setup without confirmation").action(async (options) => {
|
|
5878
|
+
try {
|
|
5879
|
+
await handleSetupCommand(options);
|
|
5880
|
+
process.exit(0);
|
|
5881
|
+
} catch (err) {
|
|
5882
|
+
console.error(
|
|
5883
|
+
`\u274C dexto setup command failed: ${err}. Check logs in ~/.dexto/logs/dexto.log for more information`
|
|
5884
|
+
);
|
|
5885
|
+
process.exit(1);
|
|
5886
|
+
}
|
|
5887
|
+
});
|
|
5888
|
+
program.command("install [agents...]").description("Install agents from the registry").option("--all", "Install all available agents from registry").option("--no-inject-preferences", "Skip injecting global preferences into installed agents").option("--force", "Force reinstall even if agent is already installed").action(async (agents = [], options) => {
|
|
5889
|
+
try {
|
|
5890
|
+
await handleInstallCommand(agents, options);
|
|
5891
|
+
process.exit(0);
|
|
5892
|
+
} catch (err) {
|
|
5893
|
+
console.error(`\u274C dexto install command failed: ${err}`);
|
|
5894
|
+
process.exit(1);
|
|
5895
|
+
}
|
|
5896
|
+
});
|
|
5897
|
+
program.command("uninstall [agents...]").description("Uninstall agents from the local installation").option("--all", "Uninstall all installed agents").option("--force", "Force uninstall even if agent is protected (e.g., default-agent)").action(async (agents, options) => {
|
|
5898
|
+
try {
|
|
5899
|
+
await handleUninstallCommand(agents, options);
|
|
5900
|
+
process.exit(0);
|
|
5901
|
+
} catch (err) {
|
|
5902
|
+
console.error(`\u274C dexto uninstall command failed: ${err}`);
|
|
5903
|
+
process.exit(1);
|
|
5904
|
+
}
|
|
5905
|
+
});
|
|
5906
|
+
program.command("list-agents").description("List available and installed agents").option("--verbose", "Show detailed agent information").option("--installed", "Show only installed agents").option("--available", "Show only available agents").action(async (options) => {
|
|
5907
|
+
try {
|
|
5908
|
+
await handleListAgentsCommand(options);
|
|
5909
|
+
process.exit(0);
|
|
5910
|
+
} catch (err) {
|
|
5911
|
+
console.error(`\u274C dexto list-agents command failed: ${err}`);
|
|
5912
|
+
process.exit(1);
|
|
5913
|
+
}
|
|
5914
|
+
});
|
|
5915
|
+
program.command("which <agent>").description("Show the path to an agent").action(async (agent) => {
|
|
5916
|
+
try {
|
|
5917
|
+
await handleWhichCommand(agent);
|
|
5918
|
+
process.exit(0);
|
|
5919
|
+
} catch (err) {
|
|
5920
|
+
console.error(`\u274C dexto which command failed: ${err}`);
|
|
5921
|
+
process.exit(1);
|
|
5922
|
+
}
|
|
5923
|
+
});
|
|
4378
5924
|
program.command("mcp").description(
|
|
4379
5925
|
"Start Dexto as an MCP server. Use --group-servers to aggregate and re-expose tools from configured MCP servers. In the future, this command will expose the agent as an MCP server by default."
|
|
4380
5926
|
).option("-s, --strict", "Require all MCP server connections to succeed").option(
|
|
@@ -4390,9 +5936,14 @@ program.command("mcp").description(
|
|
|
4390
5936
|
process.exit(1);
|
|
4391
5937
|
}
|
|
4392
5938
|
const globalOpts = program.opts();
|
|
4393
|
-
const
|
|
5939
|
+
const nameOrPath = globalOpts.agent;
|
|
5940
|
+
const configPath = await resolveAgentPath(
|
|
5941
|
+
nameOrPath,
|
|
5942
|
+
globalOpts.autoInstall !== false,
|
|
5943
|
+
true
|
|
5944
|
+
);
|
|
4394
5945
|
const config = await loadAgentConfig(configPath);
|
|
4395
|
-
console.log(`\u{1F4C4} Loading Dexto config from: ${
|
|
5946
|
+
console.log(`\u{1F4C4} Loading Dexto config from: ${configPath}`);
|
|
4396
5947
|
if (!config.mcpServers || Object.keys(config.mcpServers).length === 0) {
|
|
4397
5948
|
console.error(
|
|
4398
5949
|
"\u274C No MCP servers configured. Please configure mcpServers in your config file."
|
|
@@ -4424,38 +5975,30 @@ program.argument(
|
|
|
4424
5975
|
"[prompt...]",
|
|
4425
5976
|
"Natural-language prompt to run once. If not passed, dexto will start as an interactive CLI"
|
|
4426
5977
|
).description(
|
|
4427
|
-
|
|
5978
|
+
'Dexto CLI allows you to talk to Dexto, build custom AI Agents, build complex AI applications like Cursor, and more.\n\nRun dexto interactive CLI with `dexto` or run a one-shot prompt with `dexto -p "<prompt>"` or `dexto "<prompt>"`\nStart with a new session using `dexto --new-session [sessionId]`\nRun dexto web UI with `dexto --mode web`\nRun dexto as a server (REST APIs + WebSockets) with `dexto --mode server`\nRun dexto as a discord bot with `dexto --mode discord`\nRun dexto as a telegram bot with `dexto --mode telegram`\nRun dexto agent as an MCP server with `dexto --mode mcp`\nRun dexto as an MCP server aggregator with `dexto mcp --group-servers`\n\nCheck subcommands for more features. Check https://github.com/truffle-ai/dexto for documentation on how to customize dexto and other examples'
|
|
4428
5979
|
).action(async (prompt = []) => {
|
|
4429
|
-
if (!
|
|
5980
|
+
if (!existsSync4(".env")) {
|
|
4430
5981
|
logger.debug("WARNING: .env file not found; copy .env.example and set your API keys.");
|
|
4431
5982
|
}
|
|
4432
|
-
const
|
|
4433
|
-
|
|
4434
|
-
|
|
4435
|
-
|
|
4436
|
-
|
|
4437
|
-
|
|
4438
|
-
|
|
4439
|
-
|
|
4440
|
-
|
|
4441
|
-
if (
|
|
4442
|
-
|
|
4443
|
-
|
|
4444
|
-
|
|
4445
|
-
|
|
4446
|
-
|
|
4447
|
-
|
|
4448
|
-
|
|
4449
|
-
} else {
|
|
4450
|
-
console.error(chalk15.red("\n\u274C API key setup required to continue."));
|
|
4451
|
-
}
|
|
4452
|
-
process.exit(0);
|
|
5983
|
+
const opts = program.opts();
|
|
5984
|
+
let headlessInput = void 0;
|
|
5985
|
+
if (opts.prompt !== void 0 && String(opts.prompt).trim() !== "") {
|
|
5986
|
+
headlessInput = String(opts.prompt);
|
|
5987
|
+
} else if (opts.prompt !== void 0) {
|
|
5988
|
+
console.error(
|
|
5989
|
+
"\u274C For headless one-shot mode, prompt cannot be empty. Provide a non-empty prompt with -p/--prompt or use positional argument."
|
|
5990
|
+
);
|
|
5991
|
+
process.exit(1);
|
|
5992
|
+
} else if (prompt.length > 0) {
|
|
5993
|
+
if (prompt.length === 1) {
|
|
5994
|
+
headlessInput = prompt[0];
|
|
5995
|
+
} else {
|
|
5996
|
+
console.error(
|
|
5997
|
+
'\u274C For headless one-shot mode, pass the prompt in double quotes as a single argument (e.g., "say hello") or use -p/--prompt.'
|
|
5998
|
+
);
|
|
5999
|
+
process.exit(1);
|
|
4453
6000
|
}
|
|
4454
|
-
dotenv3.config();
|
|
4455
|
-
console.log(chalk15.green("\n\u2728 API key configured! Starting Dexto...\n"));
|
|
4456
6001
|
}
|
|
4457
|
-
const opts = program.opts();
|
|
4458
|
-
const headlessInput = prompt.join(" ") || void 0;
|
|
4459
6002
|
if (opts.model) {
|
|
4460
6003
|
let provider;
|
|
4461
6004
|
try {
|
|
@@ -4480,25 +6023,54 @@ program.argument(
|
|
|
4480
6023
|
} catch (err) {
|
|
4481
6024
|
handleCliOptionsError(err);
|
|
4482
6025
|
}
|
|
6026
|
+
let validatedConfig;
|
|
6027
|
+
let resolvedPath;
|
|
6028
|
+
try {
|
|
6029
|
+
if (opts.agent && isPath(opts.agent)) {
|
|
6030
|
+
resolvedPath = await resolveAgentPath(opts.agent, opts.autoInstall !== false, true);
|
|
6031
|
+
} else {
|
|
6032
|
+
if (opts.agent) {
|
|
6033
|
+
const registry = getAgentRegistry();
|
|
6034
|
+
if (!registry.hasAgent(opts.agent)) {
|
|
6035
|
+
console.error(`\u274C Agent '${opts.agent}' not found in registry`);
|
|
6036
|
+
const available = Object.keys(registry.getAvailableAgents());
|
|
6037
|
+
if (available.length > 0) {
|
|
6038
|
+
console.log(`\u{1F4CB} Available agents: ${available.join(", ")}`);
|
|
6039
|
+
} else {
|
|
6040
|
+
console.log("\u{1F4CB} No agents available in registry");
|
|
6041
|
+
}
|
|
6042
|
+
process.exit(1);
|
|
6043
|
+
}
|
|
6044
|
+
}
|
|
6045
|
+
if (await requiresSetup()) {
|
|
6046
|
+
if (opts.interactive === false) {
|
|
6047
|
+
console.error("\u274C Setup required but --no-interactive flag is set.");
|
|
6048
|
+
console.error("\u{1F4A1} Run `dexto setup` to configure preferences first.");
|
|
6049
|
+
process.exit(1);
|
|
6050
|
+
}
|
|
6051
|
+
await handleSetupCommand({ interactive: true });
|
|
6052
|
+
}
|
|
6053
|
+
resolvedPath = await resolveAgentPath(opts.agent, opts.autoInstall !== false, true);
|
|
6054
|
+
}
|
|
6055
|
+
const rawConfig = await loadAgentConfig(resolvedPath);
|
|
6056
|
+
const mergedConfig = applyCLIOverrides(rawConfig, opts);
|
|
6057
|
+
validatedConfig = await validateAgentConfig(mergedConfig, opts.interactive !== false);
|
|
6058
|
+
} catch (err) {
|
|
6059
|
+
console.error(`\u274C Failed to load configuration: ${err}`);
|
|
6060
|
+
process.exit(1);
|
|
6061
|
+
}
|
|
4483
6062
|
let agent;
|
|
4484
6063
|
try {
|
|
4485
|
-
|
|
4486
|
-
console.log(`\u{1F680} Initializing Dexto with config: ${resolveConfigPath(configPath)}`);
|
|
4487
|
-
const cfg = await loadAgentConfig(configPath);
|
|
4488
|
-
const cliOverrides = {
|
|
4489
|
-
model: opts.model,
|
|
4490
|
-
provider: opts.provider,
|
|
4491
|
-
router: opts.router,
|
|
4492
|
-
apiKey: opts.apiKey
|
|
4493
|
-
};
|
|
6064
|
+
console.log(`\u{1F680} Initializing Dexto with config: ${resolvedPath}`);
|
|
4494
6065
|
process.env.DEXTO_RUN_MODE = opts.mode;
|
|
4495
|
-
|
|
4496
|
-
|
|
4497
|
-
|
|
6066
|
+
if (opts.strict && validatedConfig.mcpServers) {
|
|
6067
|
+
for (const [_serverName, serverConfig] of Object.entries(
|
|
6068
|
+
validatedConfig.mcpServers
|
|
6069
|
+
)) {
|
|
4498
6070
|
serverConfig.connectionMode = "strict";
|
|
4499
6071
|
}
|
|
4500
6072
|
}
|
|
4501
|
-
agent = new DextoAgent(
|
|
6073
|
+
agent = new DextoAgent(validatedConfig, opts.agent);
|
|
4502
6074
|
await agent.start();
|
|
4503
6075
|
if (opts.newSession !== void 0) {
|
|
4504
6076
|
try {
|
|
@@ -4519,7 +6091,7 @@ program.argument(
|
|
|
4519
6091
|
}
|
|
4520
6092
|
switch (opts.mode) {
|
|
4521
6093
|
case "cli": {
|
|
4522
|
-
const { CLIToolConfirmationSubscriber } = await import("./cli-confirmation-handler-
|
|
6094
|
+
const { CLIToolConfirmationSubscriber } = await import("./cli-confirmation-handler-GJHPLGOL.js");
|
|
4523
6095
|
const cliSubscriber = new CLIToolConfirmationSubscriber();
|
|
4524
6096
|
cliSubscriber.subscribe(agent.agentEventBus);
|
|
4525
6097
|
logger.info("Setting up CLI event subscriptions...");
|
|
@@ -4598,7 +6170,7 @@ program.argument(
|
|
|
4598
6170
|
defaultBaseUrl: "stdio://local-dexto"
|
|
4599
6171
|
},
|
|
4600
6172
|
agentCardConfig
|
|
4601
|
-
// preserve overrides from agent
|
|
6173
|
+
// preserve overrides from agent file
|
|
4602
6174
|
);
|
|
4603
6175
|
const mcpTransport = await createMcpTransport("stdio");
|
|
4604
6176
|
await initializeMcpServer(agent, agentCardData, mcpTransport);
|