agdi 3.3.0 → 3.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-A5UTYKKM.js +45 -0
- package/dist/event-bus-Q3WCETQQ.js +10 -0
- package/dist/index.js +289 -117
- package/package.json +1 -1
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
// src/core/event-bus.ts
|
|
2
|
+
import { EventEmitter } from "events";
|
|
3
|
+
var agentEventBus = new EventEmitter();
|
|
4
|
+
agentEventBus.setMaxListeners(50);
|
|
5
|
+
var thoughtFlushTimeout = null;
|
|
6
|
+
var pendingThought = null;
|
|
7
|
+
function emitAgentEvent(event) {
|
|
8
|
+
const fullEvent = {
|
|
9
|
+
...event,
|
|
10
|
+
timestamp: Date.now()
|
|
11
|
+
};
|
|
12
|
+
if (event.type === "thought") {
|
|
13
|
+
pendingThought = fullEvent;
|
|
14
|
+
if (!thoughtFlushTimeout) {
|
|
15
|
+
thoughtFlushTimeout = setTimeout(() => {
|
|
16
|
+
if (pendingThought) {
|
|
17
|
+
agentEventBus.emit("agent_event", pendingThought);
|
|
18
|
+
pendingThought = null;
|
|
19
|
+
}
|
|
20
|
+
thoughtFlushTimeout = null;
|
|
21
|
+
}, 100);
|
|
22
|
+
}
|
|
23
|
+
} else {
|
|
24
|
+
agentEventBus.emit("agent_event", fullEvent);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function cleanupEventBus() {
|
|
28
|
+
agentEventBus.removeAllListeners();
|
|
29
|
+
if (thoughtFlushTimeout) {
|
|
30
|
+
clearTimeout(thoughtFlushTimeout);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
process.on("exit", cleanupEventBus);
|
|
34
|
+
process.on("SIGINT", cleanupEventBus);
|
|
35
|
+
process.on("uncaughtException", (err) => {
|
|
36
|
+
console.error("Uncaught exception:", err);
|
|
37
|
+
cleanupEventBus();
|
|
38
|
+
process.exit(1);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
export {
|
|
42
|
+
agentEventBus,
|
|
43
|
+
emitAgentEvent,
|
|
44
|
+
cleanupEventBus
|
|
45
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
agentEventBus,
|
|
4
|
+
emitAgentEvent
|
|
5
|
+
} from "./chunk-A5UTYKKM.js";
|
|
2
6
|
import {
|
|
3
7
|
loadConfig,
|
|
4
8
|
saveConfig
|
|
@@ -339,9 +343,9 @@ var errorGradient = gradient([THEME.red, "#b91c1c"]);
|
|
|
339
343
|
var goldGradient = gradient([THEME.yellow, "#fbbf24"]);
|
|
340
344
|
async function renderBanner(version = "v2.6.0") {
|
|
341
345
|
console.clear();
|
|
342
|
-
const text = await new Promise((
|
|
346
|
+
const text = await new Promise((resolve2) => {
|
|
343
347
|
figlet("AGDI", { font: "Slant" }, (err, data) => {
|
|
344
|
-
|
|
348
|
+
resolve2(data || "AGDI");
|
|
345
349
|
});
|
|
346
350
|
});
|
|
347
351
|
console.log(brandGradient.multiline(text));
|
|
@@ -1572,7 +1576,7 @@ async function runProject(targetDir) {
|
|
|
1572
1576
|
console.log(chalk6.yellow("\u{1F4E6} Installing dependencies...\n"));
|
|
1573
1577
|
const installSpinner = ora3("Running npm install...").start();
|
|
1574
1578
|
try {
|
|
1575
|
-
await new Promise((
|
|
1579
|
+
await new Promise((resolve2, reject) => {
|
|
1576
1580
|
const install = spawn("npm", ["install"], {
|
|
1577
1581
|
cwd: absoluteDir,
|
|
1578
1582
|
stdio: "inherit",
|
|
@@ -1581,7 +1585,7 @@ async function runProject(targetDir) {
|
|
|
1581
1585
|
install.on("close", (code) => {
|
|
1582
1586
|
if (code === 0) {
|
|
1583
1587
|
installSpinner.succeed("Dependencies installed!");
|
|
1584
|
-
|
|
1588
|
+
resolve2();
|
|
1585
1589
|
} else {
|
|
1586
1590
|
installSpinner.fail("npm install failed");
|
|
1587
1591
|
reject(new Error(`npm install exited with code ${code}`));
|
|
@@ -2072,6 +2076,13 @@ var BaseAgent = class {
|
|
|
2072
2076
|
* Log a message (if verbose)
|
|
2073
2077
|
*/
|
|
2074
2078
|
log(message, type = "info") {
|
|
2079
|
+
emitAgentEvent({
|
|
2080
|
+
type: "log",
|
|
2081
|
+
agentName: this.name,
|
|
2082
|
+
role: this.role,
|
|
2083
|
+
message,
|
|
2084
|
+
metadata: { type }
|
|
2085
|
+
});
|
|
2075
2086
|
if (!this.verbose) return;
|
|
2076
2087
|
const prefix = `[${this.name}]`;
|
|
2077
2088
|
switch (type) {
|
|
@@ -2090,10 +2101,29 @@ var BaseAgent = class {
|
|
|
2090
2101
|
}
|
|
2091
2102
|
/**
|
|
2092
2103
|
* Generate a response from the LLM
|
|
2104
|
+
* Includes timeout protection to prevent indefinite hanging
|
|
2093
2105
|
*/
|
|
2094
|
-
async think(prompt) {
|
|
2106
|
+
async think(prompt, timeoutMs = 12e4) {
|
|
2095
2107
|
this.log("Thinking...", "info");
|
|
2096
|
-
|
|
2108
|
+
emitAgentEvent({
|
|
2109
|
+
type: "thought",
|
|
2110
|
+
agentName: this.name,
|
|
2111
|
+
role: this.role,
|
|
2112
|
+
message: `Analyzing task...`
|
|
2113
|
+
});
|
|
2114
|
+
const response = await Promise.race([
|
|
2115
|
+
this.llm.generate(prompt, this.getSystemPrompt()),
|
|
2116
|
+
new Promise(
|
|
2117
|
+
(_, reject) => setTimeout(() => reject(new Error(`LLM request timed out after ${timeoutMs / 1e3}s`)), timeoutMs)
|
|
2118
|
+
)
|
|
2119
|
+
]);
|
|
2120
|
+
const summary = response.text.split("\n")[0].substring(0, 80) + "...";
|
|
2121
|
+
emitAgentEvent({
|
|
2122
|
+
type: "thought",
|
|
2123
|
+
agentName: this.name,
|
|
2124
|
+
role: this.role,
|
|
2125
|
+
message: `Generated response: ${summary}`
|
|
2126
|
+
});
|
|
2097
2127
|
this.collectSourcesFromText(response.text);
|
|
2098
2128
|
return response.text;
|
|
2099
2129
|
}
|
|
@@ -3124,12 +3154,17 @@ Requirements:
|
|
|
3124
3154
|
}
|
|
3125
3155
|
try {
|
|
3126
3156
|
const prodFlag = production ? "--prod" : "";
|
|
3127
|
-
const command = `npx vercel ${prodFlag} --yes
|
|
3128
|
-
this.log(`Running: npx vercel ${prodFlag} --yes
|
|
3157
|
+
const command = `npx vercel ${prodFlag} --yes`;
|
|
3158
|
+
this.log(`Running: npx vercel ${prodFlag} --yes`, "info");
|
|
3129
3159
|
const { stdout, stderr } = await execAsync2(command, {
|
|
3130
3160
|
cwd,
|
|
3131
|
-
timeout: 3e5
|
|
3161
|
+
timeout: 3e5,
|
|
3132
3162
|
// 5 minutes
|
|
3163
|
+
env: {
|
|
3164
|
+
...process.env,
|
|
3165
|
+
VERCEL_TOKEN: this.vercelToken
|
|
3166
|
+
// Pass via env, not CLI (security)
|
|
3167
|
+
}
|
|
3133
3168
|
});
|
|
3134
3169
|
const output = stdout + stderr;
|
|
3135
3170
|
const urlMatch = output.match(/https:\/\/[^\s]+\.vercel\.app/);
|
|
@@ -3179,10 +3214,15 @@ Requirements:
|
|
|
3179
3214
|
try {
|
|
3180
3215
|
const prodFlag = production ? "--prod" : "";
|
|
3181
3216
|
const outputDir = await detectBuildOutputDir(cwd);
|
|
3182
|
-
const command = `npx netlify deploy ${prodFlag} --
|
|
3217
|
+
const command = `npx netlify deploy ${prodFlag} --dir=${outputDir}`;
|
|
3183
3218
|
const { stdout, stderr } = await execAsync2(command, {
|
|
3184
3219
|
cwd,
|
|
3185
|
-
timeout: 3e5
|
|
3220
|
+
timeout: 3e5,
|
|
3221
|
+
env: {
|
|
3222
|
+
...process.env,
|
|
3223
|
+
NETLIFY_AUTH_TOKEN: this.netlifyToken
|
|
3224
|
+
// Pass via env, not CLI (security)
|
|
3225
|
+
}
|
|
3186
3226
|
});
|
|
3187
3227
|
const output = stdout + (stderr || "");
|
|
3188
3228
|
const urlMatch = output.match(/https:\/\/[^\s]+\.netlify\.app/);
|
|
@@ -3568,6 +3608,14 @@ ${qaResult.errors?.join("\n")}`,
|
|
|
3568
3608
|
*/
|
|
3569
3609
|
async executeTask(task) {
|
|
3570
3610
|
this.log(`[${task.assignee}] ${task.title}`, "task");
|
|
3611
|
+
const { emitAgentEvent: emitAgentEvent2 } = await import("./event-bus-Q3WCETQQ.js");
|
|
3612
|
+
emitAgentEvent2({
|
|
3613
|
+
type: "handoff",
|
|
3614
|
+
agentName: task.assignee,
|
|
3615
|
+
// e.g. "frontend"
|
|
3616
|
+
role: task.assignee,
|
|
3617
|
+
message: `Taking control for task: ${task.title}`
|
|
3618
|
+
});
|
|
3571
3619
|
await this.appendTrace("task_start", {
|
|
3572
3620
|
id: task.id,
|
|
3573
3621
|
title: task.title,
|
|
@@ -3615,10 +3663,21 @@ ${qaResult.errors?.join("\n")}`,
|
|
|
3615
3663
|
}
|
|
3616
3664
|
/**
|
|
3617
3665
|
* Write generated files to disk
|
|
3666
|
+
* SECURITY: All paths are validated to prevent directory traversal attacks
|
|
3618
3667
|
*/
|
|
3619
3668
|
async writeFiles(files, filesCreated, filesModified) {
|
|
3669
|
+
const workspaceRoot = path5.resolve(this.config.workspaceRoot);
|
|
3620
3670
|
for (const file of files) {
|
|
3621
|
-
const
|
|
3671
|
+
const validationResult = this.validateFilePath(file.path, workspaceRoot);
|
|
3672
|
+
if (!validationResult.valid) {
|
|
3673
|
+
this.log(`\u{1F6E1}\uFE0F Blocked unsafe path: ${file.path} - ${validationResult.error}`, "error");
|
|
3674
|
+
await this.appendTrace("file_blocked", {
|
|
3675
|
+
path: file.path,
|
|
3676
|
+
reason: validationResult.error
|
|
3677
|
+
});
|
|
3678
|
+
continue;
|
|
3679
|
+
}
|
|
3680
|
+
const fullPath = validationResult.resolvedPath;
|
|
3622
3681
|
const dir = path5.dirname(fullPath);
|
|
3623
3682
|
try {
|
|
3624
3683
|
await fs5.mkdir(dir, { recursive: true });
|
|
@@ -3633,7 +3692,7 @@ ${qaResult.errors?.join("\n")}`,
|
|
|
3633
3692
|
}
|
|
3634
3693
|
await fs5.writeFile(fullPath, file.content, "utf-8");
|
|
3635
3694
|
const afterContent = file.content;
|
|
3636
|
-
const outputPath = path5.join(this.runDir, "outputs",
|
|
3695
|
+
const outputPath = path5.join(this.runDir, "outputs", validationResult.normalizedPath);
|
|
3637
3696
|
await fs5.mkdir(path5.dirname(outputPath), { recursive: true });
|
|
3638
3697
|
await fs5.writeFile(outputPath, file.content, "utf-8");
|
|
3639
3698
|
const beforeHash = beforeContent ? this.hashContent(beforeContent) : null;
|
|
@@ -3660,6 +3719,54 @@ ${qaResult.errors?.join("\n")}`,
|
|
|
3660
3719
|
}
|
|
3661
3720
|
}
|
|
3662
3721
|
}
|
|
3722
|
+
/**
|
|
3723
|
+
* Validate a file path to prevent directory traversal attacks
|
|
3724
|
+
* SECURITY: Critical function - blocks paths that escape workspace root
|
|
3725
|
+
*/
|
|
3726
|
+
validateFilePath(filePath, workspaceRoot) {
|
|
3727
|
+
if (path5.isAbsolute(filePath)) {
|
|
3728
|
+
return {
|
|
3729
|
+
valid: false,
|
|
3730
|
+
error: "Absolute paths not allowed"
|
|
3731
|
+
};
|
|
3732
|
+
}
|
|
3733
|
+
const normalizedPath = path5.normalize(filePath).replace(/^(\.\.[\\/])+/, "");
|
|
3734
|
+
const resolvedPath = path5.resolve(workspaceRoot, filePath);
|
|
3735
|
+
if (filePath.includes("..")) {
|
|
3736
|
+
if (!resolvedPath.startsWith(workspaceRoot + path5.sep) && resolvedPath !== workspaceRoot) {
|
|
3737
|
+
return {
|
|
3738
|
+
valid: false,
|
|
3739
|
+
error: "Path traversal blocked: cannot escape workspace root"
|
|
3740
|
+
};
|
|
3741
|
+
}
|
|
3742
|
+
}
|
|
3743
|
+
if (!resolvedPath.startsWith(workspaceRoot)) {
|
|
3744
|
+
return {
|
|
3745
|
+
valid: false,
|
|
3746
|
+
error: "Path traversal blocked: resolved path outside workspace"
|
|
3747
|
+
};
|
|
3748
|
+
}
|
|
3749
|
+
const sensitivePatterns = [
|
|
3750
|
+
/^\.git\//,
|
|
3751
|
+
/^node_modules\//,
|
|
3752
|
+
/^\.env$/,
|
|
3753
|
+
/^\.env\./,
|
|
3754
|
+
/\/\.git\//
|
|
3755
|
+
];
|
|
3756
|
+
for (const pattern of sensitivePatterns) {
|
|
3757
|
+
if (pattern.test(normalizedPath)) {
|
|
3758
|
+
return {
|
|
3759
|
+
valid: false,
|
|
3760
|
+
error: `Blocked write to sensitive path: ${normalizedPath}`
|
|
3761
|
+
};
|
|
3762
|
+
}
|
|
3763
|
+
}
|
|
3764
|
+
return {
|
|
3765
|
+
valid: true,
|
|
3766
|
+
resolvedPath,
|
|
3767
|
+
normalizedPath
|
|
3768
|
+
};
|
|
3769
|
+
}
|
|
3663
3770
|
async initializeRun(userPrompt) {
|
|
3664
3771
|
this.runId = uuidv42();
|
|
3665
3772
|
this.runDir = path5.join(this.config.workspaceRoot, "runs", this.runId);
|
|
@@ -4154,142 +4261,207 @@ import { render } from "ink";
|
|
|
4154
4261
|
// src/ui/tui.tsx
|
|
4155
4262
|
import { useState, useEffect } from "react";
|
|
4156
4263
|
import { Box, Text, useApp } from "ink";
|
|
4157
|
-
import BigText from "ink-big-text";
|
|
4158
4264
|
import TextInput from "ink-text-input";
|
|
4159
4265
|
import fs7 from "fs";
|
|
4160
4266
|
import path7 from "path";
|
|
4267
|
+
import os from "os";
|
|
4161
4268
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
4269
|
+
var THEME2 = {
|
|
4270
|
+
bg: "black",
|
|
4271
|
+
border: "gray",
|
|
4272
|
+
accent: "blue",
|
|
4273
|
+
text: "white",
|
|
4274
|
+
dim: "gray",
|
|
4275
|
+
success: "green"
|
|
4276
|
+
};
|
|
4162
4277
|
var BootScreen = ({ onComplete }) => {
|
|
4163
4278
|
const [progress, setProgress] = useState(0);
|
|
4164
|
-
const [log, setLog] = useState("");
|
|
4165
|
-
const logs = [
|
|
4166
|
-
"Initializing core systems...",
|
|
4167
|
-
"Loading neural modules...",
|
|
4168
|
-
"Connecting to Agdi Cloud...",
|
|
4169
|
-
"Verifying agent credentials...",
|
|
4170
|
-
"Mounting virtual filesystem...",
|
|
4171
|
-
"Establishing secure uplink...",
|
|
4172
|
-
"Syncing global state...",
|
|
4173
|
-
"Boot sequence complete."
|
|
4174
|
-
];
|
|
4175
4279
|
useEffect(() => {
|
|
4176
4280
|
const timer = setInterval(() => {
|
|
4177
4281
|
setProgress((prev) => {
|
|
4178
|
-
const next = prev +
|
|
4282
|
+
const next = prev + 10;
|
|
4179
4283
|
if (next >= 100) {
|
|
4180
4284
|
clearInterval(timer);
|
|
4181
|
-
setTimeout(onComplete,
|
|
4285
|
+
setTimeout(onComplete, 100);
|
|
4182
4286
|
return 100;
|
|
4183
4287
|
}
|
|
4184
4288
|
return next;
|
|
4185
4289
|
});
|
|
4186
|
-
|
|
4187
|
-
setLog(logs[index] || logs[logs.length - 1]);
|
|
4188
|
-
}, 30);
|
|
4290
|
+
}, 20);
|
|
4189
4291
|
return () => clearInterval(timer);
|
|
4190
|
-
}, [
|
|
4191
|
-
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
/* @__PURE__ */ jsx(Box, { marginY: 1, children: /* @__PURE__ */ jsxs(Text, { color: "cyan", children: [
|
|
4197
|
-
bar,
|
|
4198
|
-
" ",
|
|
4199
|
-
progress,
|
|
4200
|
-
"%"
|
|
4201
|
-
] }) }),
|
|
4202
|
-
/* @__PURE__ */ jsxs(Text, { color: "gray", children: [
|
|
4203
|
-
"\u279C ",
|
|
4204
|
-
log
|
|
4292
|
+
}, []);
|
|
4293
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", height: 15, children: [
|
|
4294
|
+
/* @__PURE__ */ jsx(Text, { color: THEME2.accent, bold: true, children: "Initializing Agdi Workspace..." }),
|
|
4295
|
+
/* @__PURE__ */ jsxs(Text, { color: THEME2.dim, children: [
|
|
4296
|
+
"\u2588",
|
|
4297
|
+
"\u2588".repeat(progress / 5)
|
|
4205
4298
|
] })
|
|
4206
4299
|
] });
|
|
4207
4300
|
};
|
|
4208
|
-
var
|
|
4209
|
-
|
|
4210
|
-
|
|
4211
|
-
|
|
4212
|
-
|
|
4213
|
-
setFiles(list);
|
|
4214
|
-
} catch {
|
|
4215
|
-
setFiles(["<Error reading dir>"]);
|
|
4216
|
-
}
|
|
4217
|
-
}, [cwd]);
|
|
4218
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", title: " FILESYSTEM ", width: "30%", children: [
|
|
4219
|
-
/* @__PURE__ */ jsxs(Text, { color: "cyan", bold: true, children: [
|
|
4220
|
-
" ",
|
|
4221
|
-
cwd
|
|
4222
|
-
] }),
|
|
4223
|
-
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginTop: 1, children: [
|
|
4224
|
-
files.map((f, i) => /* @__PURE__ */ jsxs(Text, { color: "white", children: [
|
|
4225
|
-
fs7.statSync(path7.join(cwd, f)).isDirectory() ? "\u{1F4C1} " : "\u{1F4C4} ",
|
|
4226
|
-
f
|
|
4227
|
-
] }, i)),
|
|
4228
|
-
files.length === 0 && /* @__PURE__ */ jsx(Text, { color: "gray", children: " (empty)" })
|
|
4229
|
-
] })
|
|
4301
|
+
var Sidebar = ({ history }) => {
|
|
4302
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: 25, borderStyle: "single", borderColor: THEME2.border, paddingX: 1, children: [
|
|
4303
|
+
/* @__PURE__ */ jsx(Box, { marginBottom: 1, borderStyle: "single", borderColor: THEME2.accent, justifyContent: "center", children: /* @__PURE__ */ jsx(Text, { bold: true, children: "+ New task" }) }),
|
|
4304
|
+
/* @__PURE__ */ jsx(Text, { color: THEME2.dim, bold: true, children: "RECENTS" }),
|
|
4305
|
+
/* @__PURE__ */ jsx(Box, { flexDirection: "column", marginTop: 0, children: history.length === 0 ? /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "No recent sessions" }) : history.map((h, i) => /* @__PURE__ */ jsx(Text, { color: THEME2.text, children: h }, i)) })
|
|
4230
4306
|
] });
|
|
4231
4307
|
};
|
|
4232
|
-
var
|
|
4233
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column",
|
|
4234
|
-
|
|
4235
|
-
/* @__PURE__ */
|
|
4236
|
-
|
|
4237
|
-
|
|
4238
|
-
"
|
|
4239
|
-
|
|
4240
|
-
|
|
4241
|
-
|
|
4242
|
-
|
|
4243
|
-
|
|
4308
|
+
var ContextPanel = ({ files, agentStatus }) => {
|
|
4309
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width: 30, borderStyle: "single", borderColor: THEME2.border, paddingX: 1, children: [
|
|
4310
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
4311
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Context" }),
|
|
4312
|
+
/* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "WORKING FILES" }),
|
|
4313
|
+
files.length === 0 ? /* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "None yet." }) : files.slice(0, 5).map((f, i) => /* @__PURE__ */ jsxs(Text, { color: THEME2.accent, children: [
|
|
4314
|
+
"\u{1F4C4} ",
|
|
4315
|
+
f
|
|
4316
|
+
] }, i))
|
|
4317
|
+
] }),
|
|
4318
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, borderStyle: "single", borderColor: THEME2.dim, padding: 0, children: [
|
|
4319
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: " Plugins" }),
|
|
4320
|
+
/* @__PURE__ */ jsx(Text, { color: THEME2.success, children: "\u2022 Scheduler" }),
|
|
4321
|
+
/* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: " Run scheduled jobs" })
|
|
4322
|
+
] }),
|
|
4323
|
+
/* @__PURE__ */ jsxs(Box, { flexDirection: "column", children: [
|
|
4324
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: "Status" }),
|
|
4325
|
+
/* @__PURE__ */ jsxs(Text, { color: agentStatus === "IDLE" ? THEME2.dim : THEME2.success, children: [
|
|
4326
|
+
"\u2022 ",
|
|
4327
|
+
agentStatus
|
|
4328
|
+
] })
|
|
4329
|
+
] })
|
|
4244
4330
|
] });
|
|
4245
4331
|
};
|
|
4246
|
-
var
|
|
4332
|
+
var ChatArea = ({ history, onSend, placeholder }) => {
|
|
4247
4333
|
const [query, setQuery] = useState("");
|
|
4248
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", borderStyle: "
|
|
4249
|
-
/* @__PURE__ */ jsx(Box, {
|
|
4250
|
-
|
|
4251
|
-
/* @__PURE__ */ jsx(Text, {
|
|
4334
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", flexGrow: 1, borderStyle: "single", borderColor: THEME2.border, marginLeft: 0, marginRight: 0, children: [
|
|
4335
|
+
/* @__PURE__ */ jsx(Box, { justifyContent: "center", borderStyle: "single", borderBottomColor: THEME2.border, borderTop: false, borderLeft: false, borderRight: false, paddingBottom: 0, children: /* @__PURE__ */ jsx(Text, { bold: true, children: "Start a conversation" }) }),
|
|
4336
|
+
/* @__PURE__ */ jsx(Box, { flexDirection: "column", flexGrow: 1, padding: 1, justifyContent: "flex-end", children: history.length === 0 ? /* @__PURE__ */ jsxs(Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", height: 10, children: [
|
|
4337
|
+
/* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "Describe what you want to do," }),
|
|
4338
|
+
/* @__PURE__ */ jsx(Text, { color: THEME2.dim, children: "and Agdi will take it from there." })
|
|
4339
|
+
] }) : history.slice(-8).map((msg, i) => /* @__PURE__ */ jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [
|
|
4340
|
+
/* @__PURE__ */ jsx(Text, { bold: true, color: msg.role === "user" ? THEME2.text : THEME2.accent, children: msg.role === "user" ? "You" : "Agdi" }),
|
|
4341
|
+
/* @__PURE__ */ jsx(Text, { color: THEME2.text, children: msg.text })
|
|
4252
4342
|
] }, i)) }),
|
|
4253
|
-
/* @__PURE__ */
|
|
4254
|
-
|
|
4255
|
-
|
|
4256
|
-
|
|
4257
|
-
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
)
|
|
4266
|
-
] })
|
|
4343
|
+
/* @__PURE__ */ jsx(Box, { borderStyle: "round", borderColor: THEME2.dim, paddingX: 1, marginX: 1, marginBottom: 1, children: /* @__PURE__ */ jsx(
|
|
4344
|
+
TextInput,
|
|
4345
|
+
{
|
|
4346
|
+
value: query,
|
|
4347
|
+
onChange: setQuery,
|
|
4348
|
+
onSubmit: (val) => {
|
|
4349
|
+
onSend(val);
|
|
4350
|
+
setQuery("");
|
|
4351
|
+
},
|
|
4352
|
+
placeholder
|
|
4353
|
+
}
|
|
4354
|
+
) })
|
|
4267
4355
|
] });
|
|
4268
4356
|
};
|
|
4269
4357
|
var Dashboard = () => {
|
|
4270
4358
|
const { exit } = useApp();
|
|
4271
|
-
const [
|
|
4359
|
+
const [cwd, setCwd] = useState(process.cwd());
|
|
4272
4360
|
const [chatHistory, setChatHistory] = useState([]);
|
|
4273
|
-
const
|
|
4274
|
-
const
|
|
4361
|
+
const [step, setStep] = useState("safety");
|
|
4362
|
+
const [pendingAction, setPendingAction] = useState(null);
|
|
4363
|
+
const [activeFiles, setActiveFiles] = useState([]);
|
|
4364
|
+
const [agentStatus, setAgentStatus] = useState("IDLE");
|
|
4365
|
+
useEffect(() => {
|
|
4366
|
+
const handleEvent = (event) => {
|
|
4367
|
+
if (event.type === "handoff") {
|
|
4368
|
+
setAgentStatus(event.role.toUpperCase());
|
|
4369
|
+
}
|
|
4370
|
+
if (event.type === "thought") {
|
|
4371
|
+
if (!event.message.startsWith("Analyzing")) {
|
|
4372
|
+
setChatHistory((prev) => [...prev, { role: "system", text: `[${event.agentName}] ${event.message}` }]);
|
|
4373
|
+
}
|
|
4374
|
+
}
|
|
4375
|
+
if (event.message.includes("Created:")) {
|
|
4376
|
+
const filename = event.message.split("Created:")[1].trim();
|
|
4377
|
+
setActiveFiles((prev) => [filename, ...prev].slice(0, 5));
|
|
4378
|
+
}
|
|
4379
|
+
};
|
|
4380
|
+
agentEventBus.on("agent_event", handleEvent);
|
|
4381
|
+
return () => {
|
|
4382
|
+
agentEventBus.off("agent_event", handleEvent);
|
|
4383
|
+
};
|
|
4384
|
+
}, []);
|
|
4385
|
+
useEffect(() => {
|
|
4386
|
+
if (step === "safety") {
|
|
4387
|
+
const home = os.homedir();
|
|
4388
|
+
const root = path7.parse(cwd).root;
|
|
4389
|
+
const isUnsafe = cwd === home || cwd === root;
|
|
4390
|
+
if (isUnsafe) {
|
|
4391
|
+
setChatHistory([
|
|
4392
|
+
{ role: "system", text: `\u26A0\uFE0F Safety Check: Running in ${cwd}` },
|
|
4393
|
+
{ role: "ai", text: "This directory is too broad. Should I create a new project folder? (yes/no)" }
|
|
4394
|
+
]);
|
|
4395
|
+
setPendingAction("safety_confirm");
|
|
4396
|
+
} else {
|
|
4397
|
+
setStep("prompt");
|
|
4398
|
+
}
|
|
4399
|
+
}
|
|
4400
|
+
}, []);
|
|
4401
|
+
const handleCommand = async (cmd) => {
|
|
4275
4402
|
if (cmd === "/exit") exit();
|
|
4276
4403
|
setChatHistory((prev) => [...prev, { role: "user", text: cmd }]);
|
|
4277
|
-
|
|
4278
|
-
|
|
4279
|
-
|
|
4280
|
-
|
|
4281
|
-
|
|
4404
|
+
if (step === "safety" && pendingAction === "safety_confirm") {
|
|
4405
|
+
if (cmd.toLowerCase().startsWith("y")) {
|
|
4406
|
+
setChatHistory((prev) => [...prev, { role: "ai", text: "Name your project:" }]);
|
|
4407
|
+
setPendingAction("create_folder");
|
|
4408
|
+
} else {
|
|
4409
|
+
setChatHistory((prev) => [...prev, { role: "ai", text: "Proceeding in current directory." }]);
|
|
4410
|
+
setStep("prompt");
|
|
4411
|
+
setPendingAction(null);
|
|
4412
|
+
}
|
|
4413
|
+
return;
|
|
4414
|
+
}
|
|
4415
|
+
if (step === "safety" && pendingAction === "create_folder") {
|
|
4416
|
+
const newPath = path7.join(cwd, cmd.replace(/[^a-z0-9-_]/gi, "-"));
|
|
4417
|
+
try {
|
|
4418
|
+
if (!fs7.existsSync(newPath)) fs7.mkdirSync(newPath);
|
|
4419
|
+
process.chdir(newPath);
|
|
4420
|
+
setCwd(newPath);
|
|
4421
|
+
setChatHistory((prev) => [...prev, { role: "system", text: `\u{1F4C2} Switched to ${newPath}` }]);
|
|
4422
|
+
setStep("prompt");
|
|
4423
|
+
setPendingAction(null);
|
|
4424
|
+
} catch (e) {
|
|
4425
|
+
setChatHistory((prev) => [...prev, { role: "system", text: `Error: ${e}` }]);
|
|
4426
|
+
}
|
|
4427
|
+
return;
|
|
4428
|
+
}
|
|
4429
|
+
if (step === "prompt") {
|
|
4430
|
+
setStep("active");
|
|
4431
|
+
setAgentStatus("MANAGER");
|
|
4432
|
+
const activeConfig = getActiveProvider();
|
|
4433
|
+
if (!activeConfig) {
|
|
4434
|
+
setChatHistory((prev) => [...prev, { role: "system", text: '\u274C No API key found. Run "agdi auth" first.' }]);
|
|
4435
|
+
return;
|
|
4436
|
+
}
|
|
4437
|
+
const llm = createLLMProvider(activeConfig.provider, {
|
|
4438
|
+
apiKey: activeConfig.apiKey,
|
|
4439
|
+
model: activeConfig.model
|
|
4440
|
+
});
|
|
4441
|
+
runSquadCommand(cmd, llm, {
|
|
4442
|
+
output: cwd,
|
|
4443
|
+
verbose: false,
|
|
4444
|
+
deploy: false
|
|
4445
|
+
}).then(() => {
|
|
4446
|
+
setAgentStatus("IDLE");
|
|
4447
|
+
setChatHistory((prev) => [...prev, { role: "ai", text: "Task completed." }]);
|
|
4448
|
+
}).catch((err) => {
|
|
4449
|
+
setAgentStatus("ERROR");
|
|
4450
|
+
setChatHistory((prev) => [...prev, { role: "system", text: `Error: ${err.message}` }]);
|
|
4451
|
+
});
|
|
4452
|
+
}
|
|
4282
4453
|
};
|
|
4283
|
-
return /* @__PURE__ */ jsxs(Box, { flexDirection: "
|
|
4284
|
-
/* @__PURE__ */
|
|
4285
|
-
|
|
4286
|
-
|
|
4287
|
-
|
|
4288
|
-
|
|
4289
|
-
|
|
4290
|
-
|
|
4291
|
-
|
|
4292
|
-
|
|
4454
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "row", height: 30, padding: 1, children: [
|
|
4455
|
+
/* @__PURE__ */ jsx(Sidebar, { history: ["New session - " + (/* @__PURE__ */ new Date()).toLocaleDateString()] }),
|
|
4456
|
+
/* @__PURE__ */ jsx(
|
|
4457
|
+
ChatArea,
|
|
4458
|
+
{
|
|
4459
|
+
history: chatHistory,
|
|
4460
|
+
onSend: handleCommand,
|
|
4461
|
+
placeholder: step === "prompt" ? "Ask Agdi..." : "Reply..."
|
|
4462
|
+
}
|
|
4463
|
+
),
|
|
4464
|
+
/* @__PURE__ */ jsx(ContextPanel, { files: activeFiles, agentStatus })
|
|
4293
4465
|
] });
|
|
4294
4466
|
};
|
|
4295
4467
|
var App = () => {
|
|
@@ -4314,7 +4486,7 @@ ${chalk14.cyan(`/_/ |_|\\_, /\\__,_//_/ `)}
|
|
|
4314
4486
|
${chalk14.cyan(` /____/ `)}
|
|
4315
4487
|
`;
|
|
4316
4488
|
var program = new Command();
|
|
4317
|
-
program.name("agdi").description(chalk14.cyan("\u{1F9B8} The Autonomous AI Employee")).version("3.3.
|
|
4489
|
+
program.name("agdi").description(chalk14.cyan("\u{1F9B8} The Autonomous AI Employee")).version("3.3.4").option("-y, --yes", "Auto-approve all prompts (headless/CI mode)").option("-m, --minimal", "Generate only the requested file(s), not a full app").option("-d, --dry-run", "Show what would be created without writing files").option("--saas", "Generate a production SaaS blueprint (Next.js + Prisma + Postgres + Stripe)");
|
|
4318
4490
|
program.hook("preAction", (thisCommand) => {
|
|
4319
4491
|
const opts = thisCommand.opts();
|
|
4320
4492
|
if (opts.yes) {
|