delegate-team 2.0.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/LICENSE +21 -0
- package/README.md +385 -0
- package/dist/chunk-VTAPBAJE.js +545 -0
- package/dist/cli.js +864 -0
- package/dist/proxy/server.js +6 -0
- package/package.json +86 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,864 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DELEGATE_TEAM_PATH,
|
|
4
|
+
RELAY_SCRIPT,
|
|
5
|
+
ROUTER_SCRIPT,
|
|
6
|
+
VERTEX_CODER_PATH,
|
|
7
|
+
VERTEX_DIRECT_SCRIPT,
|
|
8
|
+
VERTEX_INTERACTIVE_SCRIPT,
|
|
9
|
+
VERTEX_VENV_PYTHON,
|
|
10
|
+
runServe
|
|
11
|
+
} from "./chunk-VTAPBAJE.js";
|
|
12
|
+
|
|
13
|
+
// src/cli.ts
|
|
14
|
+
import { Command } from "commander";
|
|
15
|
+
|
|
16
|
+
// src/commands/check.ts
|
|
17
|
+
import { existsSync } from "fs";
|
|
18
|
+
import { join } from "path";
|
|
19
|
+
import { homedir } from "os";
|
|
20
|
+
|
|
21
|
+
// src/utils/index.ts
|
|
22
|
+
import { spawnSync } from "child_process";
|
|
23
|
+
var C = {
|
|
24
|
+
reset: "\x1B[0m",
|
|
25
|
+
bold: "\x1B[1m",
|
|
26
|
+
dim: "\x1B[2m",
|
|
27
|
+
red: "\x1B[31m",
|
|
28
|
+
green: "\x1B[32m",
|
|
29
|
+
yellow: "\x1B[33m",
|
|
30
|
+
blue: "\x1B[34m",
|
|
31
|
+
magenta: "\x1B[35m",
|
|
32
|
+
cyan: "\x1B[36m",
|
|
33
|
+
white: "\x1B[37m",
|
|
34
|
+
bgRed: "\x1B[41m",
|
|
35
|
+
bgGreen: "\x1B[42m",
|
|
36
|
+
bgYellow: "\x1B[43m"
|
|
37
|
+
};
|
|
38
|
+
function runCmd(command, args = []) {
|
|
39
|
+
try {
|
|
40
|
+
const res = spawnSync(command, args, { encoding: "utf8" });
|
|
41
|
+
return { status: res.status, stdout: res.stdout || "", stderr: res.stderr || "" };
|
|
42
|
+
} catch (err) {
|
|
43
|
+
return { status: -1, stdout: "", stderr: err.message };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/commands/check.ts
|
|
48
|
+
import { spawnSync as spawnSync2 } from "child_process";
|
|
49
|
+
function runCheck(strict = false) {
|
|
50
|
+
console.log(`
|
|
51
|
+
${C.bold}${C.cyan}\u{1F50D} Scanning Backend Systems Health & Authentication Status...${C.reset}
|
|
52
|
+
`);
|
|
53
|
+
const results = [];
|
|
54
|
+
const gcloudCheck = runCmd("gcloud", ["auth", "print-access-token"]);
|
|
55
|
+
const gcloudAccount = runCmd("gcloud", ["config", "get-value", "account"]);
|
|
56
|
+
const vertexPythonCheck = existsSync(VERTEX_VENV_PYTHON);
|
|
57
|
+
let vertexSdkCheck = false;
|
|
58
|
+
if (vertexPythonCheck) {
|
|
59
|
+
const pyRes = spawnSync2(VERTEX_VENV_PYTHON, ["-c", "import google.genai"], { encoding: "utf8" });
|
|
60
|
+
vertexSdkCheck = pyRes.status === 0;
|
|
61
|
+
}
|
|
62
|
+
const isVertexReady = gcloudCheck.status === 0 && vertexPythonCheck && vertexSdkCheck;
|
|
63
|
+
results.push({
|
|
64
|
+
backend: "vertexcoder",
|
|
65
|
+
binary: vertexPythonCheck ? `Python venv \u2705` : `venv Missing \u274C`,
|
|
66
|
+
auth: gcloudCheck.status === 0 ? `GCP Active: ${gcloudAccount.stdout.trim()}` : `gcloud NOT Logged In \u274C`,
|
|
67
|
+
status: isVertexReady ? `${C.green}READY \u2705${C.reset}` : `${C.red}NOT READY \u274C${C.reset}`
|
|
68
|
+
});
|
|
69
|
+
const codexBin = runCmd("which", ["codex"]);
|
|
70
|
+
const codexConfig = existsSync(join(homedir(), ".codex-delegate", "config.toml"));
|
|
71
|
+
results.push({
|
|
72
|
+
backend: "codex",
|
|
73
|
+
binary: codexBin.status === 0 ? `Installed \u2705` : `Missing \u274C`,
|
|
74
|
+
auth: codexConfig ? `config.toml Present \u2705` : `~/.codex-delegate/config.toml Missing \u274C`,
|
|
75
|
+
status: codexBin.status === 0 && codexConfig ? `${C.green}READY \u2705${C.reset}` : `${C.yellow}UNCONFIGURED \u26A0\uFE0F${C.reset}`
|
|
76
|
+
});
|
|
77
|
+
const minimaxBin = runCmd("which", ["claude"]);
|
|
78
|
+
const minimaxConfig = existsSync(join(homedir(), ".minimax", ".env"));
|
|
79
|
+
results.push({
|
|
80
|
+
backend: "minimax",
|
|
81
|
+
binary: minimaxBin.status === 0 ? `Installed (claude) \u2705` : `Missing \u274C`,
|
|
82
|
+
auth: minimaxConfig ? `.env Present \u2705` : `~/.minimax/.env Missing \u274C`,
|
|
83
|
+
status: minimaxBin.status === 0 && minimaxConfig ? `${C.green}READY \u2705${C.reset}` : `${C.yellow}UNCONFIGURED \u26A0\uFE0F${C.reset}`
|
|
84
|
+
});
|
|
85
|
+
const opencodeBin = runCmd("which", ["opencode"]);
|
|
86
|
+
const opencodeConfig = existsSync(join(homedir(), ".local", "share", "opencode", "auth.json"));
|
|
87
|
+
results.push({
|
|
88
|
+
backend: "opencode",
|
|
89
|
+
binary: opencodeBin.status === 0 ? `Installed \u2705` : `Missing \u274C`,
|
|
90
|
+
auth: opencodeConfig ? `auth.json Present \u2705` : `auth.json Missing \u274C`,
|
|
91
|
+
status: opencodeBin.status === 0 && opencodeConfig ? `${C.green}READY \u2705${C.reset}` : `${C.yellow}UNCONFIGURED \u26A0\uFE0F${C.reset}`
|
|
92
|
+
});
|
|
93
|
+
const geminiBin = runCmd("which", ["gemini"]);
|
|
94
|
+
const geminiConfig = existsSync(join(homedir(), ".gemini", ".env"));
|
|
95
|
+
results.push({
|
|
96
|
+
backend: "gemini",
|
|
97
|
+
binary: geminiBin.status === 0 ? `Installed \u2705` : `Missing \u274C`,
|
|
98
|
+
auth: geminiConfig ? `.env Present \u2705` : `~/.gemini/.env Missing \u274C`,
|
|
99
|
+
status: geminiBin.status === 0 && geminiConfig ? `${C.green}READY \u2705${C.reset}` : `${C.yellow}UNCONFIGURED \u26A0\uFE0F${C.reset}`
|
|
100
|
+
});
|
|
101
|
+
results.push({
|
|
102
|
+
backend: "openrouter",
|
|
103
|
+
binary: opencodeBin.status === 0 ? `Installed (opencode) \u2705` : `Missing \u274C`,
|
|
104
|
+
auth: existsSync(join(homedir(), ".openrouter", ".env")) ? `.env Present \u2705` : `~/.openrouter/.env Missing \u274C`,
|
|
105
|
+
status: opencodeBin.status === 0 && existsSync(join(homedir(), ".openrouter", ".env")) ? `${C.green}READY \u2705${C.reset}` : `${C.yellow}UNCONFIGURED \u26A0\uFE0F${C.reset}`
|
|
106
|
+
});
|
|
107
|
+
console.log(`${C.bold}${"Backend".padEnd(15)} | ${"Binary / SDK".padEnd(23)} | ${"Config / Credentials".padEnd(45)} | Status${C.reset}`);
|
|
108
|
+
console.log("-".repeat(95));
|
|
109
|
+
for (const row of results) {
|
|
110
|
+
console.log(`${C.bold}${C.cyan}${row.backend.padEnd(15)}${C.reset} | ${row.binary.padEnd(23)} | ${row.auth.padEnd(45)} | ${row.status}`);
|
|
111
|
+
}
|
|
112
|
+
console.log("\n");
|
|
113
|
+
if (strict) {
|
|
114
|
+
const anyReady = results.some((r) => r.status.includes("READY \u2705"));
|
|
115
|
+
if (!anyReady) {
|
|
116
|
+
process.exit(1);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// src/commands/setup.ts
|
|
122
|
+
import { existsSync as existsSync2, mkdirSync, rmSync, symlinkSync, writeFileSync, lstatSync, readlinkSync } from "fs";
|
|
123
|
+
import { join as join2 } from "path";
|
|
124
|
+
import { homedir as homedir2 } from "os";
|
|
125
|
+
import { spawnSync as spawnSync3 } from "child_process";
|
|
126
|
+
import readline from "readline";
|
|
127
|
+
import { randomBytes } from "crypto";
|
|
128
|
+
function runLinkSkill() {
|
|
129
|
+
console.log(`
|
|
130
|
+
${C.bold}${C.cyan}\u{1F517} Symlinking Agent Skills to Local Systems...${C.reset}`);
|
|
131
|
+
const agentSkillsDir = join2(homedir2(), ".agents", "skills");
|
|
132
|
+
const geminiSkillsDir = join2(homedir2(), ".gemini", "config", "skills");
|
|
133
|
+
const skillDirs = [agentSkillsDir, geminiSkillsDir];
|
|
134
|
+
runCmd("chmod", ["+x", RELAY_SCRIPT]);
|
|
135
|
+
runCmd("chmod", ["+x", VERTEX_DIRECT_SCRIPT]);
|
|
136
|
+
runCmd("chmod", ["+x", VERTEX_INTERACTIVE_SCRIPT]);
|
|
137
|
+
runCmd("chmod", ["+x", join2(DELEGATE_TEAM_PATH, "scripts", "opencode-router.mjs")]);
|
|
138
|
+
let linkedCount = 0;
|
|
139
|
+
for (const dir of skillDirs) {
|
|
140
|
+
if (!existsSync2(dir)) {
|
|
141
|
+
try {
|
|
142
|
+
mkdirSync(dir, { recursive: true });
|
|
143
|
+
console.log(`${C.dim}Created skills directory: ${dir}${C.reset}`);
|
|
144
|
+
} catch (err) {
|
|
145
|
+
console.error(`${C.red}Failed to create skills directory ${dir}: ${err.message}${C.reset}`);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
const delTarget = join2(dir, "delegate-team");
|
|
150
|
+
const vtxTarget = join2(dir, "vertex-coder");
|
|
151
|
+
try {
|
|
152
|
+
let shouldLink = true;
|
|
153
|
+
try {
|
|
154
|
+
const stat = lstatSync(delTarget);
|
|
155
|
+
if (stat.isSymbolicLink()) {
|
|
156
|
+
const target = readlinkSync(delTarget);
|
|
157
|
+
if (target === DELEGATE_TEAM_PATH) {
|
|
158
|
+
shouldLink = false;
|
|
159
|
+
} else {
|
|
160
|
+
rmSync(delTarget, { force: true });
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
console.log(` ${C.yellow}\u26A0\uFE0F Directory ${delTarget} exists and is not a symlink. Skipping to prevent data loss.${C.reset}`);
|
|
164
|
+
shouldLink = false;
|
|
165
|
+
}
|
|
166
|
+
} catch (e) {
|
|
167
|
+
}
|
|
168
|
+
if (shouldLink) {
|
|
169
|
+
symlinkSync(DELEGATE_TEAM_PATH, delTarget, "dir");
|
|
170
|
+
console.log(` ${C.green}\u2705 Linked delegate-team${C.reset} -> ${C.dim}${dir}/delegate-team${C.reset}`);
|
|
171
|
+
linkedCount++;
|
|
172
|
+
}
|
|
173
|
+
} catch (err) {
|
|
174
|
+
console.error(` ${C.red}\u274C Failed link delegate-team to ${dir}: ${err.message}${C.reset}`);
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
let shouldLink = true;
|
|
178
|
+
try {
|
|
179
|
+
const stat = lstatSync(vtxTarget);
|
|
180
|
+
if (stat.isSymbolicLink()) {
|
|
181
|
+
const target = readlinkSync(vtxTarget);
|
|
182
|
+
if (target === VERTEX_CODER_PATH) {
|
|
183
|
+
shouldLink = false;
|
|
184
|
+
} else {
|
|
185
|
+
rmSync(vtxTarget, { force: true });
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
console.log(` ${C.yellow}\u26A0\uFE0F Directory ${vtxTarget} exists and is not a symlink. Skipping to prevent data loss.${C.reset}`);
|
|
189
|
+
shouldLink = false;
|
|
190
|
+
}
|
|
191
|
+
} catch (e) {
|
|
192
|
+
}
|
|
193
|
+
if (shouldLink) {
|
|
194
|
+
symlinkSync(VERTEX_CODER_PATH, vtxTarget, "dir");
|
|
195
|
+
console.log(` ${C.green}\u2705 Linked vertex-coder${C.reset} -> ${C.dim}${dir}/vertex-coder${C.reset}`);
|
|
196
|
+
linkedCount++;
|
|
197
|
+
}
|
|
198
|
+
} catch (err) {
|
|
199
|
+
console.error(` ${C.red}\u274C Failed link vertex-coder to ${dir}: ${err.message}${C.reset}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (linkedCount > 0) {
|
|
203
|
+
console.log(`
|
|
204
|
+
${C.bold}${C.green}\u{1F389} Skill registration completed successfully! (${linkedCount} symlinks updated)${C.reset}
|
|
205
|
+
`);
|
|
206
|
+
} else {
|
|
207
|
+
console.error(`
|
|
208
|
+
${C.bold}${C.red}\u26A0\uFE0F Skill registration completed with errors.${C.reset}
|
|
209
|
+
`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
async function runSetup() {
|
|
213
|
+
console.log(`
|
|
214
|
+
${C.bold}${C.cyan}\u{1F680} Initiating Autopilot Onboarding Setup Wizard...${C.reset}`);
|
|
215
|
+
console.log(`${C.dim}This wizard will configure your local environment, authentications, and cloud projects automatically.${C.reset}
|
|
216
|
+
`);
|
|
217
|
+
const rl = readline.createInterface({
|
|
218
|
+
input: process.stdin,
|
|
219
|
+
output: process.stdout
|
|
220
|
+
});
|
|
221
|
+
const ask = (query) => new Promise((resolve) => rl.question(query, resolve));
|
|
222
|
+
const proxyToken = "dt-local-" + randomBytes(8).toString("hex");
|
|
223
|
+
console.log(`${C.bold}\u{1F4E6} [Step 1/6] Setting up Python Virtual Environment...${C.reset}`);
|
|
224
|
+
const venvDir = join2(VERTEX_CODER_PATH, ".venv");
|
|
225
|
+
if (!existsSync2(venvDir)) {
|
|
226
|
+
console.log(` ${C.dim}Virtual environment (.venv) not found. Creating a new one...${C.reset}`);
|
|
227
|
+
let pythonCmd = "python3";
|
|
228
|
+
const py311Check = spawnSync3("which", ["python3.11"]);
|
|
229
|
+
if (py311Check.status === 0) {
|
|
230
|
+
pythonCmd = "python3.11";
|
|
231
|
+
console.log(` ${C.dim}Using ${pythonCmd} for compatibility...${C.reset}`);
|
|
232
|
+
}
|
|
233
|
+
const venvProc = spawnSync3(pythonCmd, ["-m", "venv", venvDir], { stdio: "inherit" });
|
|
234
|
+
if (venvProc.status !== 0) {
|
|
235
|
+
console.error(` ${C.red}\u274C Failed to create virtual environment. Please install python3-venv.${C.reset}`);
|
|
236
|
+
rl.close();
|
|
237
|
+
process.exit(1);
|
|
238
|
+
}
|
|
239
|
+
console.log(` ${C.green}\u2705 Virtual environment created at: ${venvDir}${C.reset}`);
|
|
240
|
+
} else {
|
|
241
|
+
console.log(` ${C.green}\u2705 Existing virtual environment detected.${C.reset}`);
|
|
242
|
+
}
|
|
243
|
+
console.log(` ${C.dim}Installing required Python dependencies...${C.reset}`);
|
|
244
|
+
const pipPath = join2(venvDir, "bin", "pip");
|
|
245
|
+
const pipProc = spawnSync3(pipPath, ["install", "--upgrade", "pip", "google-genai", "google-cloud-dialogflow-cx", "google-auth-oauthlib", "pytest"], { stdio: "inherit" });
|
|
246
|
+
if (pipProc.status !== 0) {
|
|
247
|
+
console.error(` ${C.red}\u274C Failed to install core Python dependencies.${C.reset}`);
|
|
248
|
+
rl.close();
|
|
249
|
+
process.exit(1);
|
|
250
|
+
}
|
|
251
|
+
console.log(` ${C.green}\u2705 Core Python dependencies installed successfully!${C.reset}
|
|
252
|
+
`);
|
|
253
|
+
console.log(` ${C.dim}Attempting to install MetaGPT (requires Python <= 3.11)...${C.reset}`);
|
|
254
|
+
const metagptProc = spawnSync3(pipPath, ["install", "metagpt"], { stdio: "inherit" });
|
|
255
|
+
if (metagptProc.status !== 0) {
|
|
256
|
+
console.log(` ${C.yellow}\u26A0\uFE0F MetaGPT installation skipped (often due to Python 3.12+ or Apple Silicon faiss-cpu issues). You can install it manually later if needed.${C.reset}
|
|
257
|
+
`);
|
|
258
|
+
} else {
|
|
259
|
+
console.log(` ${C.green}\u2705 MetaGPT installed successfully!${C.reset}
|
|
260
|
+
`);
|
|
261
|
+
}
|
|
262
|
+
console.log(`${C.bold}\u{1F511} [Step 2/6] Verifying Google Cloud SDK (gcloud)...${C.reset}`);
|
|
263
|
+
const gcloudBinCheck = runCmd("which", ["gcloud"]);
|
|
264
|
+
if (gcloudBinCheck.status !== 0) {
|
|
265
|
+
console.log(` ${C.yellow}\u26A0\uFE0F Google Cloud CLI (gcloud) was not found in your system PATH.${C.reset}`);
|
|
266
|
+
console.log(` ${C.dim}To use Vertex AI, you need to install gcloud. Follow instructions at: https://cloud.google.com/sdk/docs/install${C.reset}`);
|
|
267
|
+
const proceed = await ask(` Do you want to skip gcloud checks and continue setting up other components? (y/N): `);
|
|
268
|
+
if (proceed.trim().toLowerCase() !== "y") {
|
|
269
|
+
rl.close();
|
|
270
|
+
process.exit(127);
|
|
271
|
+
}
|
|
272
|
+
console.log(` ${C.dim}Skipping gcloud verification...${C.reset}
|
|
273
|
+
`);
|
|
274
|
+
} else {
|
|
275
|
+
console.log(` ${C.green}\u2705 Google Cloud CLI detected.${C.reset}
|
|
276
|
+
`);
|
|
277
|
+
}
|
|
278
|
+
if (gcloudBinCheck.status === 0) {
|
|
279
|
+
console.log(`${C.bold}\u{1F510} [Step 3/6] Authenticating with Google Cloud...${C.reset}`);
|
|
280
|
+
const loginChoice = await ask(` Would you like to log in to gcloud and set up application credentials now? (Y/n): `);
|
|
281
|
+
if (loginChoice.trim().toLowerCase() !== "n") {
|
|
282
|
+
await runAuth();
|
|
283
|
+
} else {
|
|
284
|
+
console.log(` ${C.dim}Skipping active authentication...${C.reset}\\n`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
let selectedProjectId = "";
|
|
288
|
+
if (gcloudBinCheck.status === 0) {
|
|
289
|
+
console.log(`${C.bold}\u{1F4BC} [Step 4/6] Resolving Google Cloud Project ID...${C.reset}`);
|
|
290
|
+
console.log(` ${C.dim}Fetching active projects from your account...${C.reset}`);
|
|
291
|
+
const projectsProc = spawnSync3("gcloud", ["projects", "list", "--format=json"], { encoding: "utf8" });
|
|
292
|
+
let projects = [];
|
|
293
|
+
if (projectsProc.status === 0) {
|
|
294
|
+
try {
|
|
295
|
+
projects = JSON.parse(projectsProc.stdout.trim());
|
|
296
|
+
} catch (err) {
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
if (projects.length > 0) {
|
|
300
|
+
console.log(`
|
|
301
|
+
${C.bold}Available GCP Projects:${C.reset}`);
|
|
302
|
+
for (let i = 0; i < projects.length; i++) {
|
|
303
|
+
console.log(` [${i + 1}] ${C.cyan}${projects[i].projectId}${C.reset} (${projects[i].name || "N/A"})`);
|
|
304
|
+
}
|
|
305
|
+
console.log(` [${projects.length + 1}] Enter Project ID Manually`);
|
|
306
|
+
const pChoice = await ask(`
|
|
307
|
+
Select a project by number (1-${projects.length + 1}): `);
|
|
308
|
+
const choiceIdx = parseInt(pChoice.trim(), 10) - 1;
|
|
309
|
+
if (choiceIdx >= 0 && choiceIdx < projects.length) {
|
|
310
|
+
selectedProjectId = projects[choiceIdx].projectId;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
if (!selectedProjectId) {
|
|
314
|
+
const manualProj = await ask(` Enter your Google Cloud Project ID manually: `);
|
|
315
|
+
selectedProjectId = manualProj.trim();
|
|
316
|
+
}
|
|
317
|
+
if (!selectedProjectId) {
|
|
318
|
+
console.error(` ${C.red}\u274C No Project ID provided. Setup aborted.${C.reset}`);
|
|
319
|
+
rl.close();
|
|
320
|
+
process.exit(1);
|
|
321
|
+
}
|
|
322
|
+
console.log(` ${C.green}\u{1F3AF} Bound to GCP Project: ${C.bold}${selectedProjectId}${C.reset}
|
|
323
|
+
`);
|
|
324
|
+
} else {
|
|
325
|
+
const manualProj = await ask(`\u{1F4BC} [Step 4/6] Enter your Google Cloud Project ID manually: `);
|
|
326
|
+
selectedProjectId = manualProj.trim();
|
|
327
|
+
if (!selectedProjectId) {
|
|
328
|
+
console.error(` ${C.red}\u274C No Project ID provided. Setup aborted.${C.reset}`);
|
|
329
|
+
rl.close();
|
|
330
|
+
process.exit(1);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (gcloudBinCheck.status === 0) {
|
|
334
|
+
console.log(`${C.bold}\u2699\uFE0F [Step 5/6] Enabling APIs & Provisioning Agent...${C.reset}`);
|
|
335
|
+
const apiChoice = await ask(` Do you want to automatically enable Vertex AI & Dialogflow APIs in project '${selectedProjectId}'? (Y/n): `);
|
|
336
|
+
if (apiChoice.trim().toLowerCase() !== "n") {
|
|
337
|
+
await runGcpEnable(selectedProjectId);
|
|
338
|
+
}
|
|
339
|
+
const configDir2 = join2(homedir2(), ".config", "dt");
|
|
340
|
+
if (!existsSync2(configDir2)) {
|
|
341
|
+
mkdirSync(configDir2, { recursive: true });
|
|
342
|
+
}
|
|
343
|
+
const configPath2 = join2(configDir2, "config.json");
|
|
344
|
+
const configData2 = {
|
|
345
|
+
project_id: selectedProjectId,
|
|
346
|
+
location: "us-central1",
|
|
347
|
+
proxy_token: proxyToken
|
|
348
|
+
};
|
|
349
|
+
writeFileSync(configPath2, JSON.stringify(configData2, null, 2), "utf8");
|
|
350
|
+
const provisionChoice = await ask(` Do you want to provision the dt agent on GCP? (Y/n): `);
|
|
351
|
+
if (provisionChoice.trim().toLowerCase() !== "n") {
|
|
352
|
+
await runVertexProvision();
|
|
353
|
+
}
|
|
354
|
+
} else {
|
|
355
|
+
console.log(`${C.bold}\u2699\uFE0F [Step 5/6] Skipping API enablement & Agent provisioning (no gcloud).${C.reset}\\n`);
|
|
356
|
+
}
|
|
357
|
+
console.log(`${C.bold}\u{1F517} [Step 6/6] Linking Agent Skills & Writing Global Configuration...${C.reset}`);
|
|
358
|
+
runLinkSkill();
|
|
359
|
+
const configDir = join2(homedir2(), ".config", "dt");
|
|
360
|
+
if (!existsSync2(configDir)) {
|
|
361
|
+
mkdirSync(configDir, { recursive: true });
|
|
362
|
+
}
|
|
363
|
+
const configPath = join2(configDir, "config.json");
|
|
364
|
+
const configData = {
|
|
365
|
+
project_id: selectedProjectId,
|
|
366
|
+
location: "us-central1",
|
|
367
|
+
proxy_token: proxyToken
|
|
368
|
+
};
|
|
369
|
+
writeFileSync(configPath, JSON.stringify(configData, null, 2), "utf8");
|
|
370
|
+
console.log(` ${C.green}\u{1F4BE} Written configuration to: ${C.dim}${configPath}${C.reset}`);
|
|
371
|
+
const metagptDir = join2(homedir2(), ".metagpt");
|
|
372
|
+
if (!existsSync2(metagptDir)) {
|
|
373
|
+
mkdirSync(metagptDir, { recursive: true });
|
|
374
|
+
}
|
|
375
|
+
const metagptConfigPath = join2(metagptDir, "config2.yaml");
|
|
376
|
+
const metagptConfigContent = `llm:
|
|
377
|
+
api_type: "openai"
|
|
378
|
+
base_url: "http://127.0.0.1:3000/v1"
|
|
379
|
+
api_key: "${proxyToken}"
|
|
380
|
+
model: "google/gemini-3.1-pro-custom-tools"
|
|
381
|
+
`;
|
|
382
|
+
writeFileSync(metagptConfigPath, metagptConfigContent, "utf8");
|
|
383
|
+
console.log(` ${C.green}\u{1F4BE} Written MetaGPT configuration to: ${C.dim}${metagptConfigPath}${C.reset}`);
|
|
384
|
+
console.log(`
|
|
385
|
+
${C.bold}${C.green}\u{1F389} CONGRATULATIONS! AUTOPILOT SETUP COMPLETED SUCCESSFULLY!${C.reset}
|
|
386
|
+
--------------------------------------------------------------
|
|
387
|
+
${C.bold}Your settings:${C.reset}
|
|
388
|
+
\u{1F3AF} Bound Project: ${C.bold}${C.cyan}${selectedProjectId}${C.reset}
|
|
389
|
+
\u{1F4CD} Location: ${C.bold}${C.cyan}us-central1${C.reset}
|
|
390
|
+
\u{1F4BC} Python env: ${C.dim}${venvDir}${C.reset}
|
|
391
|
+
\u{1F4C2} Config Path: ${C.dim}${configPath}${C.reset}
|
|
392
|
+
|
|
393
|
+
${C.bold}Ready commands:${C.reset}
|
|
394
|
+
${C.cyan}dt check${C.reset} Check status of all backends
|
|
395
|
+
${C.cyan}dt vx interactive "prompt"${C.reset} Launch Interactive Vertex Coding Agent
|
|
396
|
+
${C.cyan}dt run "prompt"${C.reset} Dispatch task with routing and failover
|
|
397
|
+
|
|
398
|
+
Thank you for using dt / delegate-team!
|
|
399
|
+
`);
|
|
400
|
+
rl.close();
|
|
401
|
+
}
|
|
402
|
+
async function runAuth() {
|
|
403
|
+
console.log(`
|
|
404
|
+
${C.bold}${C.cyan}\u{1F510} Authenticating with Google Cloud...${C.reset}`);
|
|
405
|
+
console.log(` ${C.dim}Opening browser for user authentication...${C.reset}`);
|
|
406
|
+
const loginProc = spawnSync3("gcloud", ["auth", "login"], { stdio: "inherit" });
|
|
407
|
+
if (loginProc.status !== 0) throw new Error("gcloud auth login failed");
|
|
408
|
+
console.log(`
|
|
409
|
+
${C.dim}Opening browser for application-default credentials (ADC) authentication...${C.reset}`);
|
|
410
|
+
const adcProc = spawnSync3("gcloud", ["auth", "application-default", "login"], { stdio: "inherit" });
|
|
411
|
+
if (adcProc.status !== 0) throw new Error("gcloud auth application-default login failed");
|
|
412
|
+
console.log(` ${C.green}\u2705 Google Cloud authentication completed.${C.reset}
|
|
413
|
+
`);
|
|
414
|
+
}
|
|
415
|
+
async function runGcpEnable(projectId) {
|
|
416
|
+
console.log(`
|
|
417
|
+
${C.bold}${C.cyan}\u2699\uFE0F Enabling APIs in project '${projectId}'...${C.reset}`);
|
|
418
|
+
console.log(` ${C.dim}Enabling Vertex AI API...${C.reset}`);
|
|
419
|
+
const aiProc = spawnSync3("gcloud", ["services", "enable", "aiplatform.googleapis.com", "--project", projectId], { stdio: "inherit" });
|
|
420
|
+
if (aiProc.status !== 0) throw new Error("Failed to enable aiplatform.googleapis.com");
|
|
421
|
+
console.log(` ${C.dim}Enabling Dialogflow API...${C.reset}`);
|
|
422
|
+
const dfProc = spawnSync3("gcloud", ["services", "enable", "dialogflow.googleapis.com", "--project", projectId], { stdio: "inherit" });
|
|
423
|
+
if (dfProc.status !== 0) throw new Error("Failed to enable dialogflow.googleapis.com");
|
|
424
|
+
console.log(` ${C.green}\u2705 GCP APIs enabled successfully.${C.reset}
|
|
425
|
+
`);
|
|
426
|
+
}
|
|
427
|
+
async function runVertexProvision() {
|
|
428
|
+
console.log(`
|
|
429
|
+
${C.bold}${C.cyan}\u{1F916} Provisioning Vertex AI Agent...${C.reset}`);
|
|
430
|
+
console.log(` ${C.dim}Running vertex-coder provisioner...${C.reset}`);
|
|
431
|
+
const provisionScript = join2(VERTEX_CODER_PATH, "provision_agent.py");
|
|
432
|
+
const pythonPath = VERTEX_VENV_PYTHON;
|
|
433
|
+
if (!existsSync2(pythonPath)) {
|
|
434
|
+
console.error(` ${C.red}\u274C Python virtual environment not found at ${pythonPath}. Please run 'dt setup' first.${C.reset}`);
|
|
435
|
+
process.exit(1);
|
|
436
|
+
}
|
|
437
|
+
if (!existsSync2(provisionScript)) {
|
|
438
|
+
console.error(` ${C.red}\u274C Provision script not found at ${provisionScript}.${C.reset}`);
|
|
439
|
+
process.exit(1);
|
|
440
|
+
}
|
|
441
|
+
const proc = spawnSync3(pythonPath, [provisionScript], { stdio: "inherit" });
|
|
442
|
+
if (proc.status === 0) {
|
|
443
|
+
console.log(` ${C.green}\u2705 Vertex AI Agent provisioned successfully.${C.reset}
|
|
444
|
+
`);
|
|
445
|
+
} else {
|
|
446
|
+
console.error(` ${C.red}\u274C Failed to provision Vertex AI Agent.${C.reset}
|
|
447
|
+
`);
|
|
448
|
+
throw new Error("Vertex AI Agent provisioning failed");
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
// src/commands/run.ts
|
|
453
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync, writeFileSync as writeFileSync2 } from "fs";
|
|
454
|
+
import { unlinkSync } from "fs";
|
|
455
|
+
import { join as join3 } from "path";
|
|
456
|
+
import { tmpdir } from "os";
|
|
457
|
+
import { spawnSync as spawnSync4 } from "child_process";
|
|
458
|
+
|
|
459
|
+
// src/fallback/ring.ts
|
|
460
|
+
var FALLBACK_RING = {
|
|
461
|
+
codex: ["vertexcoder", "minimax", "opencode", "gemini"],
|
|
462
|
+
minimax: ["vertexcoder", "codex", "opencode", "gemini"],
|
|
463
|
+
opencode: ["vertexcoder", "codex", "minimax", "gemini"],
|
|
464
|
+
vertexcoder: ["codex", "minimax", "opencode", "gemini"],
|
|
465
|
+
gemini: ["vertexcoder", "codex", "minimax", "opencode"],
|
|
466
|
+
openrouter: ["vertexcoder", "codex", "minimax", "opencode", "gemini"]
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
// src/commands/run.ts
|
|
470
|
+
function runVertex(mode, args) {
|
|
471
|
+
if (!existsSync3(VERTEX_VENV_PYTHON)) {
|
|
472
|
+
console.error(`${C.bold}${C.red}Error: Python virtual environment (.venv) not found at: ${VERTEX_VENV_PYTHON}${C.reset}`);
|
|
473
|
+
console.error(`${C.dim}Please verify your vertex-coder setup or re-run 'dt check' to inspect.${C.reset}`);
|
|
474
|
+
process.exit(127);
|
|
475
|
+
}
|
|
476
|
+
if (mode === "direct") {
|
|
477
|
+
if (args.length < 2) {
|
|
478
|
+
console.error(`${C.bold}${C.red}Usage: dt vx direct <file_path> "<prompt>" [model]${C.reset}`);
|
|
479
|
+
process.exit(2);
|
|
480
|
+
}
|
|
481
|
+
const proc = spawnSync4(VERTEX_VENV_PYTHON, [VERTEX_DIRECT_SCRIPT, ...args], { stdio: "inherit" });
|
|
482
|
+
process.exit(proc.status ?? 1);
|
|
483
|
+
} else if (mode === "interactive") {
|
|
484
|
+
if (args.length < 1) {
|
|
485
|
+
console.error(`${C.bold}${C.red}Usage: dt vx interactive "<complex_prompt>" [model]${C.reset}`);
|
|
486
|
+
process.exit(2);
|
|
487
|
+
}
|
|
488
|
+
const proc = spawnSync4(VERTEX_VENV_PYTHON, [VERTEX_INTERACTIVE_SCRIPT, ...args], { stdio: "inherit" });
|
|
489
|
+
process.exit(proc.status ?? 1);
|
|
490
|
+
} else {
|
|
491
|
+
console.error(`${C.bold}${C.red}Error: Unknown mode "${mode}". Supported: direct, interactive${C.reset}`);
|
|
492
|
+
console.error(` ${C.dim}Example: dt vx direct index.html "Add background transition"`);
|
|
493
|
+
console.error(` ${C.dim}Example: dt vx interactive "Setup vitest suite"`);
|
|
494
|
+
process.exit(2);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
function runDispatch(rawPrompt, options) {
|
|
498
|
+
let backend = options.backend;
|
|
499
|
+
if (options.team) {
|
|
500
|
+
backend = "metagpt";
|
|
501
|
+
}
|
|
502
|
+
let briefFile = options.brief;
|
|
503
|
+
const forwardArgs = [];
|
|
504
|
+
let isTempBrief = false;
|
|
505
|
+
if (rawPrompt && !briefFile) {
|
|
506
|
+
const tempBriefDir = join3(tmpdir(), "dt-briefs");
|
|
507
|
+
if (!existsSync3(tempBriefDir)) mkdirSync2(tempBriefDir, { recursive: true });
|
|
508
|
+
briefFile = join3(tempBriefDir, `brief-${Date.now()}.txt`);
|
|
509
|
+
const briefContent = `TASK CLI_TASK: ${rawPrompt}
|
|
510
|
+
NAV: use the code graph
|
|
511
|
+
CHANGE:
|
|
512
|
+
${rawPrompt}
|
|
513
|
+
`;
|
|
514
|
+
writeFileSync2(briefFile, briefContent, "utf8");
|
|
515
|
+
forwardArgs.push("--brief", briefFile ?? "");
|
|
516
|
+
isTempBrief = true;
|
|
517
|
+
console.log(`${C.dim}Generated temporary brief file: ${briefFile}${C.reset}`);
|
|
518
|
+
} else if (briefFile) {
|
|
519
|
+
forwardArgs.push("--brief", briefFile);
|
|
520
|
+
}
|
|
521
|
+
if (!briefFile) {
|
|
522
|
+
console.error(`${C.bold}${C.red}Error: Please specify either a prompt or a brief file using --brief <file_path>${C.reset}`);
|
|
523
|
+
process.exit(2);
|
|
524
|
+
}
|
|
525
|
+
const briefText = readFileSync(briefFile, "utf8");
|
|
526
|
+
let forceMetagpt = false;
|
|
527
|
+
let autoRouted = false;
|
|
528
|
+
if (!backend) {
|
|
529
|
+
console.log(`${C.dim}Running OpenCode Router to evaluate task complexity...${C.reset}`);
|
|
530
|
+
try {
|
|
531
|
+
const routerProc = spawnSync4(process.execPath, [ROUTER_SCRIPT, "route"], {
|
|
532
|
+
input: briefText,
|
|
533
|
+
encoding: "utf8",
|
|
534
|
+
timeout: 5e3
|
|
535
|
+
});
|
|
536
|
+
if (routerProc.status === 0) {
|
|
537
|
+
const routeData = JSON.parse(routerProc.stdout.trim());
|
|
538
|
+
console.log(`${C.bold}${C.cyan}Router Complexity Score: ${routeData.score}${C.reset}`);
|
|
539
|
+
if (routeData.score >= 8) {
|
|
540
|
+
backend = "metagpt";
|
|
541
|
+
autoRouted = true;
|
|
542
|
+
console.log(` \u{1F3AF} Routing to: ${C.bold}${C.magenta}metagpt${C.reset} (MetaGPT Team Orchestrator for complex tasks)`);
|
|
543
|
+
} else if (routeData.score > 5) {
|
|
544
|
+
backend = "vertexcoder";
|
|
545
|
+
console.log(` \u{1F3AF} Routing to: ${C.bold}${C.green}vertexcoder${C.reset} (Premium Gemini AI via GCP SDK)`);
|
|
546
|
+
} else if (routeData.score > 0) {
|
|
547
|
+
backend = "opencode";
|
|
548
|
+
console.log(` \u{1F3AF} Routing to: ${C.bold}${C.green}opencode${C.reset} (Balanced OpenCode Router)`);
|
|
549
|
+
} else {
|
|
550
|
+
backend = "minimax";
|
|
551
|
+
console.log(` \u{1F3AF} Routing to: ${C.bold}${C.green}minimax${C.reset} (Fast MiniMax Agent)`);
|
|
552
|
+
}
|
|
553
|
+
} else {
|
|
554
|
+
backend = "vertexcoder";
|
|
555
|
+
console.log(`Router returned non-zero status, defaulting to: ${C.bold}${C.green}vertexcoder${C.reset}`);
|
|
556
|
+
}
|
|
557
|
+
} catch {
|
|
558
|
+
backend = "vertexcoder";
|
|
559
|
+
console.log(`Router evaluation failed, defaulting to: ${C.bold}${C.green}vertexcoder${C.reset}`);
|
|
560
|
+
}
|
|
561
|
+
if (!backend) {
|
|
562
|
+
backend = "vertexcoder";
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
if (backend === "metagpt") {
|
|
566
|
+
forceMetagpt = true;
|
|
567
|
+
} else {
|
|
568
|
+
forwardArgs.push("--backend", backend);
|
|
569
|
+
}
|
|
570
|
+
let success = false;
|
|
571
|
+
if (forceMetagpt) {
|
|
572
|
+
console.log(`
|
|
573
|
+
${C.bold}${C.magenta}\u{1F680} Dispatching task to team orchestrator: [METAGPT]${C.reset}`);
|
|
574
|
+
const dtCli = process.argv[1] || process.argv[0];
|
|
575
|
+
const metagptArgs = ["metagpt", rawPrompt || briefText];
|
|
576
|
+
if (autoRouted || !options.allowInstall) {
|
|
577
|
+
metagptArgs.push("--no-install");
|
|
578
|
+
}
|
|
579
|
+
metagptArgs.push("--workspace-only");
|
|
580
|
+
if (options.approveWrite) {
|
|
581
|
+
metagptArgs.push("--approve-write");
|
|
582
|
+
}
|
|
583
|
+
const proc = spawnSync4(process.execPath, [dtCli, ...metagptArgs], { stdio: "inherit" });
|
|
584
|
+
success = proc.status === 0;
|
|
585
|
+
} else {
|
|
586
|
+
let activeBackend = backend;
|
|
587
|
+
let currentChain = [activeBackend, ...FALLBACK_RING[activeBackend] || []];
|
|
588
|
+
for (let attempt = 0; attempt < currentChain.length; attempt++) {
|
|
589
|
+
activeBackend = currentChain[attempt];
|
|
590
|
+
console.log(`
|
|
591
|
+
${C.bold}${C.magenta}\u{1F680} Dispatching task to backend: [${activeBackend.toUpperCase()}] (Attempt ${attempt + 1}/${currentChain.length})${C.reset}`);
|
|
592
|
+
console.log(`${C.dim}relay.mjs ${forwardArgs.filter((a) => a !== "--backend" && a !== activeBackend).join(" ")} --backend ${activeBackend}${C.reset}
|
|
593
|
+
`);
|
|
594
|
+
const runArgs = [...forwardArgs];
|
|
595
|
+
const bIndex = runArgs.indexOf("--backend");
|
|
596
|
+
if (bIndex !== -1) {
|
|
597
|
+
runArgs[bIndex + 1] = activeBackend;
|
|
598
|
+
}
|
|
599
|
+
const start = Date.now();
|
|
600
|
+
const proc = spawnSync4(process.execPath, [RELAY_SCRIPT, ...runArgs], { stdio: "inherit" });
|
|
601
|
+
const durationSec = ((Date.now() - start) / 1e3).toFixed(1);
|
|
602
|
+
if (proc.status === 0) {
|
|
603
|
+
console.log(`
|
|
604
|
+
${C.bold}${C.green}\u2705 Task completed successfully by [${activeBackend.toUpperCase()}] in ${durationSec}s!${C.reset}
|
|
605
|
+
`);
|
|
606
|
+
success = true;
|
|
607
|
+
break;
|
|
608
|
+
} else {
|
|
609
|
+
console.log(`
|
|
610
|
+
${C.bold}${C.yellow}\u26A0\uFE0F Backend [${activeBackend.toUpperCase()}] failed or exited with status code ${proc.status || "N/A"} after ${durationSec}s.${C.reset}`);
|
|
611
|
+
const nextBackend = currentChain[attempt + 1];
|
|
612
|
+
if (nextBackend) {
|
|
613
|
+
console.log(`${C.bold}${C.yellow}\u{1F504} Activating Automated Failover Ring \u2192 Routing to next backup: [${nextBackend.toUpperCase()}]${C.reset}`);
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
if (isTempBrief && existsSync3(briefFile)) {
|
|
619
|
+
try {
|
|
620
|
+
unlinkSync(briefFile);
|
|
621
|
+
} catch {
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
if (!success) {
|
|
625
|
+
console.error(`
|
|
626
|
+
${C.bold}${C.red}\u274C Failover Ring Exhausted! All available backends have failed to execute this task.${C.reset}`);
|
|
627
|
+
console.error(` ${C.bold}${C.yellow}Orchestrator Directive: Please finish the task manually using SELF.${C.reset}
|
|
628
|
+
`);
|
|
629
|
+
process.exit(1);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
// src/commands/metagpt.ts
|
|
634
|
+
import { spawn } from "child_process";
|
|
635
|
+
import { join as join5 } from "path";
|
|
636
|
+
import { existsSync as existsSync5, readFileSync as readFileSync3 } from "fs";
|
|
637
|
+
|
|
638
|
+
// src/utils/tracer.ts
|
|
639
|
+
import { homedir as homedir3 } from "os";
|
|
640
|
+
import { join as join4 } from "path";
|
|
641
|
+
import { existsSync as existsSync4, mkdirSync as mkdirSync3, writeFileSync as writeFileSync3, readFileSync as readFileSync2 } from "fs";
|
|
642
|
+
var TraceManager = class {
|
|
643
|
+
tracesDir;
|
|
644
|
+
constructor() {
|
|
645
|
+
this.tracesDir = join4(homedir3(), ".config", "dt", "traces");
|
|
646
|
+
if (!existsSync4(this.tracesDir)) {
|
|
647
|
+
mkdirSync3(this.tracesDir, { recursive: true });
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
createTrace(parentId = "claude-code-session") {
|
|
651
|
+
const inheritedParentId = process.env.DT_TRACE_ID || parentId;
|
|
652
|
+
const inheritedDepth = process.env.DT_EXECUTION_DEPTH ? parseInt(process.env.DT_EXECUTION_DEPTH, 10) + 1 : 1;
|
|
653
|
+
const canCallMetagpt = process.env.DT_CAN_CALL_METAGPT ? process.env.DT_CAN_CALL_METAGPT === "true" : true;
|
|
654
|
+
const inheritedMaxRoles = process.env.DT_MAX_ROLES ? parseInt(process.env.DT_MAX_ROLES, 10) : 5;
|
|
655
|
+
const traceId = `dt-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}-${Date.now()}`;
|
|
656
|
+
const trace = {
|
|
657
|
+
trace_id: traceId,
|
|
658
|
+
parent_id: inheritedParentId,
|
|
659
|
+
controller: "claude-code",
|
|
660
|
+
executor: "metagpt",
|
|
661
|
+
depth_control: {
|
|
662
|
+
max_depth: 2,
|
|
663
|
+
current_depth: inheritedDepth,
|
|
664
|
+
can_delegate: true,
|
|
665
|
+
can_call_metagpt: canCallMetagpt
|
|
666
|
+
// Respect environment restriction
|
|
667
|
+
},
|
|
668
|
+
budget: {
|
|
669
|
+
max_roles: inheritedMaxRoles,
|
|
670
|
+
max_rounds: 2,
|
|
671
|
+
max_tokens_total: 12e4,
|
|
672
|
+
max_runtime_seconds: 900,
|
|
673
|
+
fallback_budget: 2
|
|
674
|
+
},
|
|
675
|
+
roles: [],
|
|
676
|
+
final_status: "initialized",
|
|
677
|
+
commit_allowed: false
|
|
678
|
+
// Enforce Review Gate
|
|
679
|
+
};
|
|
680
|
+
if (!canCallMetagpt) {
|
|
681
|
+
trace.depth_control.current_depth = trace.depth_control.max_depth + 1;
|
|
682
|
+
}
|
|
683
|
+
this.saveTrace(trace);
|
|
684
|
+
return trace;
|
|
685
|
+
}
|
|
686
|
+
saveTrace(trace) {
|
|
687
|
+
const tracePath = join4(this.tracesDir, `${trace.trace_id}.json`);
|
|
688
|
+
writeFileSync3(tracePath, JSON.stringify(trace, null, 2), "utf8");
|
|
689
|
+
}
|
|
690
|
+
loadTrace(traceId) {
|
|
691
|
+
const tracePath = join4(this.tracesDir, `${traceId}.json`);
|
|
692
|
+
if (existsSync4(tracePath)) {
|
|
693
|
+
return JSON.parse(readFileSync2(tracePath, "utf8"));
|
|
694
|
+
}
|
|
695
|
+
return null;
|
|
696
|
+
}
|
|
697
|
+
};
|
|
698
|
+
|
|
699
|
+
// src/commands/metagpt.ts
|
|
700
|
+
async function runMetaGPTRouter(prompt, options = {}) {
|
|
701
|
+
const venvDir = join5(VERTEX_CODER_PATH, ".venv");
|
|
702
|
+
const pythonPath = join5(venvDir, "bin", "python3");
|
|
703
|
+
const metagptAdapterPath = join5(VERTEX_CODER_PATH, "dt_metagpt_adapter.py");
|
|
704
|
+
const tracer = new TraceManager();
|
|
705
|
+
const trace = tracer.createTrace();
|
|
706
|
+
if (trace.depth_control.current_depth > trace.depth_control.max_depth) {
|
|
707
|
+
console.error(`
|
|
708
|
+
\u274C Delegation Blocked: Maximum execution depth (${trace.depth_control.max_depth}) exceeded.`);
|
|
709
|
+
return 1;
|
|
710
|
+
}
|
|
711
|
+
console.log(`
|
|
712
|
+
\u{1F916} Launching MetaGPT AI Software Company...`);
|
|
713
|
+
console.log(`\u{1F4CB} Trace ID: ${trace.trace_id}`);
|
|
714
|
+
console.log(`\u{1F4CB} Prompt: "${prompt}"
|
|
715
|
+
`);
|
|
716
|
+
const metagptArgs = [metagptAdapterPath, prompt, "--project-path", process.cwd()];
|
|
717
|
+
const metagptEnv = {};
|
|
718
|
+
if (options.planOnly) {
|
|
719
|
+
console.log(`\u{1F6E1}\uFE0F Guardrail Active: [--plan-only] MetaGPT will generate plan/architecture only.`);
|
|
720
|
+
metagptArgs.push("--plan-only");
|
|
721
|
+
metagptEnv["DT_PLAN_ONLY"] = "true";
|
|
722
|
+
}
|
|
723
|
+
if (options.approveWrite) {
|
|
724
|
+
console.log(`\u{1F6E1}\uFE0F Guardrail Active: [--approve-write] Requiring human approval before disk writes.`);
|
|
725
|
+
metagptArgs.push("--approve-write");
|
|
726
|
+
metagptEnv["DT_APPROVE_WRITE"] = "true";
|
|
727
|
+
}
|
|
728
|
+
if (options.workspaceOnly) {
|
|
729
|
+
console.log(`\u{1F6E1}\uFE0F Guardrail Active: [--workspace-only] Sandboxing to workspace root.`);
|
|
730
|
+
metagptArgs.push("--workspace-only");
|
|
731
|
+
metagptEnv["DT_WORKSPACE_ONLY"] = "true";
|
|
732
|
+
}
|
|
733
|
+
if (options.noInstall) {
|
|
734
|
+
console.log(`\u{1F6E1}\uFE0F Guardrail Active: [--no-install] Package installation blocked.`);
|
|
735
|
+
metagptArgs.push("--no-install");
|
|
736
|
+
metagptEnv["DT_NO_INSTALL"] = "true";
|
|
737
|
+
}
|
|
738
|
+
if (options.dryRun) {
|
|
739
|
+
console.log(`\u{1F6E1}\uFE0F Guardrail Active: [--dry-run] Simulating workflow safely.`);
|
|
740
|
+
metagptArgs.push("--dry-run");
|
|
741
|
+
metagptEnv["DT_DRY_RUN"] = "true";
|
|
742
|
+
}
|
|
743
|
+
console.log(`\x1B[90mEnsure your dt proxy server is running (dt serve 3000) for MetaGPT to connect to the backend.\x1B[0m
|
|
744
|
+
`);
|
|
745
|
+
return new Promise((resolve) => {
|
|
746
|
+
const execEnv = {
|
|
747
|
+
...process.env,
|
|
748
|
+
...metagptEnv,
|
|
749
|
+
DT_TRACE_ID: trace.trace_id,
|
|
750
|
+
DT_MAX_ROLES: trace.budget.max_roles.toString(),
|
|
751
|
+
DT_MAX_TOKENS: trace.budget.max_tokens_total.toString(),
|
|
752
|
+
DT_EXECUTION_DEPTH: trace.depth_control.current_depth.toString(),
|
|
753
|
+
DT_CAN_CALL_METAGPT: trace.depth_control.can_call_metagpt.toString(),
|
|
754
|
+
DT_CLI_PATH: process.argv[1] || process.argv[0]
|
|
755
|
+
};
|
|
756
|
+
const child = spawn(pythonPath, metagptArgs, {
|
|
757
|
+
stdio: "inherit",
|
|
758
|
+
env: execEnv
|
|
759
|
+
});
|
|
760
|
+
child.on("error", (err) => {
|
|
761
|
+
console.error(`
|
|
762
|
+
\u274C Failed to launch MetaGPT: ${err.message}`);
|
|
763
|
+
console.error(`Did you run 'dt setup' to install metagpt in the virtual environment?`);
|
|
764
|
+
resolve(1);
|
|
765
|
+
});
|
|
766
|
+
child.on("close", (code) => {
|
|
767
|
+
if (code === 0) {
|
|
768
|
+
console.log(`
|
|
769
|
+
\u2705 MetaGPT software company completed its work.`);
|
|
770
|
+
trace.final_status = "ready_for_review";
|
|
771
|
+
} else {
|
|
772
|
+
console.log(`
|
|
773
|
+
\u26A0\uFE0F MetaGPT exited with code ${code}.`);
|
|
774
|
+
trace.final_status = "failed";
|
|
775
|
+
}
|
|
776
|
+
const contractPath = join5(process.cwd(), ".dt_aggregation_contract.json");
|
|
777
|
+
if (existsSync5(contractPath)) {
|
|
778
|
+
try {
|
|
779
|
+
const contract = JSON.parse(readFileSync3(contractPath, "utf8"));
|
|
780
|
+
if (Array.isArray(contract.roles_executed)) {
|
|
781
|
+
if (contract.roles_executed.length > 0 && typeof contract.roles_executed[0] === "object") {
|
|
782
|
+
trace.roles = contract.roles_executed;
|
|
783
|
+
} else {
|
|
784
|
+
trace.roles = contract.roles_executed.map((r) => ({ role: r, backend: "unknown", status: "completed", files_touched: contract.files_touched || [] }));
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
} catch (e) {
|
|
788
|
+
console.error(`Failed to parse aggregation contract: ${e}`);
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
tracer.saveTrace(trace);
|
|
792
|
+
resolve(code || 0);
|
|
793
|
+
});
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
// src/cli.ts
|
|
798
|
+
import fs from "fs";
|
|
799
|
+
var packageJsonPath = new URL("../package.json", import.meta.url);
|
|
800
|
+
var version = "1.0.0";
|
|
801
|
+
try {
|
|
802
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
803
|
+
version = pkg.version;
|
|
804
|
+
} catch (e) {
|
|
805
|
+
}
|
|
806
|
+
var program = new Command();
|
|
807
|
+
program.name("dt").description("Unified Multi-Backend Developer Agent Dispatch & CLI suite").version(version);
|
|
808
|
+
program.command("check").alias("status").description("Scan health, configs, and credential status of all backends").option("--strict", "Exit with code 1 if no backends are fully ready").action((options) => {
|
|
809
|
+
runCheck(options.strict);
|
|
810
|
+
});
|
|
811
|
+
program.command("doctor").description("Alias for check command").option("--strict", "Exit with code 1 if no backends are fully ready").action((options) => {
|
|
812
|
+
runCheck(options.strict);
|
|
813
|
+
});
|
|
814
|
+
program.command("link-skill").description("Automate integration by symlinking tools to local Claude/Gemini folders").action(() => {
|
|
815
|
+
runLinkSkill();
|
|
816
|
+
});
|
|
817
|
+
program.command("setup").alias("init").description("Run autopilot setup to automatically configure dependencies, auth, GCP & agents").action(() => {
|
|
818
|
+
runSetup().catch((err) => {
|
|
819
|
+
console.error("\n\u274C Setup failed:", err);
|
|
820
|
+
process.exit(1);
|
|
821
|
+
});
|
|
822
|
+
});
|
|
823
|
+
program.command("auth").description("Authenticate with Google Cloud (gcloud login)").action(() => {
|
|
824
|
+
runAuth().catch((err) => {
|
|
825
|
+
console.error("\n\u274C Auth failed:", err);
|
|
826
|
+
process.exit(1);
|
|
827
|
+
});
|
|
828
|
+
});
|
|
829
|
+
program.command("gcp-enable <project_id>").description("Enable Vertex AI and Dialogflow APIs in your GCP project").action((projectId) => {
|
|
830
|
+
runGcpEnable(projectId).catch((err) => {
|
|
831
|
+
console.error("\n\u274C GCP Enable failed:", err);
|
|
832
|
+
process.exit(1);
|
|
833
|
+
});
|
|
834
|
+
});
|
|
835
|
+
program.command("vertex-provision").description("Provision the Vertex AI agent on GCP").action(() => {
|
|
836
|
+
runVertexProvision().catch((err) => {
|
|
837
|
+
console.error("\n\u274C Vertex provision failed:", err);
|
|
838
|
+
process.exit(1);
|
|
839
|
+
});
|
|
840
|
+
});
|
|
841
|
+
program.command("vx <mode> [args...]").alias("vertex").description("Direct high-performance interface for Google Vertex AI coding agent (modes: direct, interactive)").action((mode, args) => {
|
|
842
|
+
runVertex(mode, args);
|
|
843
|
+
});
|
|
844
|
+
program.command("metagpt [prompt...]").alias("mg").description("Launch MetaGPT AI Software Company for complex multi-agent architectures").option("--plan-only", "Generate plan and architecture without writing code").option("--approve-write", "Require human approval before writing to disk").option("--workspace-only", "Strictly sandbox MetaGPT to the current workspace root").option("--no-install", "Prevent MetaGPT from installing package dependencies").option("--dry-run", "Simulate workflow without making destructive changes").action(async (promptArray, options) => {
|
|
845
|
+
const prompt = Array.isArray(promptArray) ? promptArray.join(" ") : promptArray;
|
|
846
|
+
const code = await runMetaGPTRouter(prompt, options);
|
|
847
|
+
process.exit(code);
|
|
848
|
+
});
|
|
849
|
+
program.command("run [prompt...]").alias("dispatch").description("Dispatch a task to a backend agent with automatic routing & failover").option("-b, --backend <backend>", "Specify a backend to use directly").option("--brief <file>", "Specify a brief file instead of a direct prompt").option("--team", "Force routing to the MetaGPT team orchestrator").option("--allow-install", "Allow package installation during execution").option("--approve-write", "Require human approval before writing to disk").action((promptArray, options) => {
|
|
850
|
+
const prompt = Array.isArray(promptArray) ? promptArray.join(" ") : promptArray;
|
|
851
|
+
runDispatch(prompt, options);
|
|
852
|
+
});
|
|
853
|
+
program.command("serve [port]").alias("proxy").description("Start the LLM Gateway Proxy Server").action((port) => {
|
|
854
|
+
runServe(port ? parseInt(port, 10) : 3e3);
|
|
855
|
+
});
|
|
856
|
+
if (process.argv.length > 2 && !process.argv[2].startsWith("-")) {
|
|
857
|
+
const isCommand = ["check", "status", "doctor", "link-skill", "setup", "init", "auth", "gcp-enable", "vertex-provision", "vx", "vertex", "metagpt", "mg", "run", "dispatch", "serve", "proxy", "help"].includes(process.argv[2]);
|
|
858
|
+
if (!isCommand) {
|
|
859
|
+
console.error(`\\n\u274C Error: Unknown command '${process.argv[2]}'.`);
|
|
860
|
+
console.error(`Run 'dt --help' to see available commands.\\n`);
|
|
861
|
+
process.exit(1);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
program.parse(process.argv);
|