@lazyingart/agintiflow 0.8.4 → 0.8.6
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 +2 -2
- package/docs/agent-runtime-pipe.md +4 -0
- package/package.json +1 -1
- package/public/app.js +55 -1
- package/public/index.html +4 -1
- package/public/styles.css +9 -0
- package/src/agent-runner.js +280 -25
- package/src/docker-sandbox.js +3 -1
- package/src/guardrails.js +21 -0
- package/src/interactive-cli.js +221 -75
- package/src/model-client.js +86 -16
- package/src/static-preview-server.js +105 -0
- package/src/workspace-tools.js +1 -1
- package/web.js +27 -0
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import http from "node:http";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
const root = path.resolve(process.argv[2] || process.cwd());
|
|
6
|
+
const port = Number(process.argv[3] || 0);
|
|
7
|
+
const host = "127.0.0.1";
|
|
8
|
+
|
|
9
|
+
const MIME_TYPES = new Map([
|
|
10
|
+
[".html", "text/html; charset=utf-8"],
|
|
11
|
+
[".htm", "text/html; charset=utf-8"],
|
|
12
|
+
[".css", "text/css; charset=utf-8"],
|
|
13
|
+
[".js", "text/javascript; charset=utf-8"],
|
|
14
|
+
[".mjs", "text/javascript; charset=utf-8"],
|
|
15
|
+
[".json", "application/json; charset=utf-8"],
|
|
16
|
+
[".svg", "image/svg+xml"],
|
|
17
|
+
[".png", "image/png"],
|
|
18
|
+
[".jpg", "image/jpeg"],
|
|
19
|
+
[".jpeg", "image/jpeg"],
|
|
20
|
+
[".gif", "image/gif"],
|
|
21
|
+
[".webp", "image/webp"],
|
|
22
|
+
[".pdf", "application/pdf"],
|
|
23
|
+
[".txt", "text/plain; charset=utf-8"],
|
|
24
|
+
[".md", "text/markdown; charset=utf-8"],
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
function send(res, statusCode, body, headers = {}) {
|
|
28
|
+
res.writeHead(statusCode, {
|
|
29
|
+
"Cache-Control": "no-store",
|
|
30
|
+
"X-Content-Type-Options": "nosniff",
|
|
31
|
+
...headers,
|
|
32
|
+
});
|
|
33
|
+
res.end(body);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function normalizeUrlPath(urlPath) {
|
|
37
|
+
const decoded = decodeURIComponent(urlPath.split("?")[0] || "/");
|
|
38
|
+
const withoutLeadingSlash = decoded.replace(/^\/+/, "") || "index.html";
|
|
39
|
+
return path.normalize(withoutLeadingSlash);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function isInsideRoot(target) {
|
|
43
|
+
const relative = path.relative(root, target);
|
|
44
|
+
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function isBlockedPath(relativePath) {
|
|
48
|
+
const segments = relativePath.split(path.sep).filter(Boolean);
|
|
49
|
+
const lowerBase = (segments.at(-1) || "").toLowerCase();
|
|
50
|
+
const lowerPath = segments.join("/").toLowerCase();
|
|
51
|
+
if (segments.includes(".git") || segments.includes("node_modules") || segments.includes(".sessions")) return true;
|
|
52
|
+
if (lowerBase === ".env" || lowerBase.startsWith(".env.")) return true;
|
|
53
|
+
if (lowerBase === ".npmrc" || lowerBase === ".pypirc") return true;
|
|
54
|
+
return /(^|\/)(secrets?|tokens?|passwords?|private[-_]?keys?|credentials?)(\/|\.|$)/i.test(lowerPath);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const server = http.createServer(async (req, res) => {
|
|
58
|
+
if (!["GET", "HEAD"].includes(req.method || "")) {
|
|
59
|
+
send(res, 405, "Method Not Allowed");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
try {
|
|
64
|
+
const relative = normalizeUrlPath(req.url || "/");
|
|
65
|
+
const target = path.resolve(root, relative);
|
|
66
|
+
if (!isInsideRoot(target) || isBlockedPath(path.relative(root, target))) {
|
|
67
|
+
send(res, 403, "Forbidden");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
let filePath = target;
|
|
72
|
+
const stat = await fs.stat(filePath).catch(() => null);
|
|
73
|
+
if (stat?.isDirectory()) {
|
|
74
|
+
filePath = path.join(filePath, "index.html");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const finalStat = await fs.stat(filePath).catch(() => null);
|
|
78
|
+
if (!finalStat?.isFile()) {
|
|
79
|
+
send(res, 404, "Not Found");
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const contentType = MIME_TYPES.get(path.extname(filePath).toLowerCase()) || "application/octet-stream";
|
|
84
|
+
res.writeHead(200, {
|
|
85
|
+
"Content-Type": contentType,
|
|
86
|
+
"Content-Length": finalStat.size,
|
|
87
|
+
"Cache-Control": "no-store",
|
|
88
|
+
"X-Content-Type-Options": "nosniff",
|
|
89
|
+
});
|
|
90
|
+
if (req.method === "HEAD") {
|
|
91
|
+
res.end();
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const content = await fs.readFile(filePath);
|
|
95
|
+
res.end(content);
|
|
96
|
+
} catch (error) {
|
|
97
|
+
send(res, 500, error instanceof Error ? error.message : String(error));
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
server.listen(port, host, () => {
|
|
102
|
+
const address = server.address();
|
|
103
|
+
const actualPort = typeof address === "object" && address ? address.port : port;
|
|
104
|
+
console.log(`AgInTiFlow static preview http://${host}:${actualPort}/ root=${root}`);
|
|
105
|
+
});
|
package/src/workspace-tools.js
CHANGED
|
@@ -145,7 +145,7 @@ export function summarizeWorkspaceTools(config) {
|
|
|
145
145
|
return {
|
|
146
146
|
enabled: Boolean(config.allowFileTools),
|
|
147
147
|
workspace: workspaceRoot(config),
|
|
148
|
-
tools: WORKSPACE_TOOL_NAMES,
|
|
148
|
+
tools: [...WORKSPACE_TOOL_NAMES, "open_workspace_file", "preview_workspace"],
|
|
149
149
|
writeTools: WORKSPACE_WRITE_TOOL_NAMES,
|
|
150
150
|
limits: {
|
|
151
151
|
maxReadBytes: MAX_READ_BYTES,
|
package/web.js
CHANGED
|
@@ -455,6 +455,8 @@ function createRunRecord(config, goal, existingLogs = []) {
|
|
|
455
455
|
}
|
|
456
456
|
|
|
457
457
|
function wireRun(record, config) {
|
|
458
|
+
const abortController = new AbortController();
|
|
459
|
+
record.abortController = abortController;
|
|
458
460
|
const push = (kind, message, data = {}) => {
|
|
459
461
|
record.logs.push({
|
|
460
462
|
at: new Date().toISOString(),
|
|
@@ -470,6 +472,7 @@ function wireRun(record, config) {
|
|
|
470
472
|
|
|
471
473
|
void runAgent({
|
|
472
474
|
...config,
|
|
475
|
+
abortSignal: abortController.signal,
|
|
473
476
|
onLog: (message, data = {}) => push("log", message, data),
|
|
474
477
|
onEvent: (type, data = {}) => push("event", type, data),
|
|
475
478
|
})
|
|
@@ -846,6 +849,30 @@ app.get("/api/runs/:sessionId", async (req, res) => {
|
|
|
846
849
|
res.json(stored);
|
|
847
850
|
});
|
|
848
851
|
|
|
852
|
+
app.post("/api/runs/:sessionId/stop", async (req, res) => {
|
|
853
|
+
const run = runs.get(req.params.sessionId);
|
|
854
|
+
if (!run) {
|
|
855
|
+
res.status(404).json({ error: "Run not found." });
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
if (run.status !== "running") {
|
|
859
|
+
res.status(409).json({ error: "Run is not active.", status: run.status });
|
|
860
|
+
return;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
run.abortController?.abort(new Error("Stopped from web UI."));
|
|
864
|
+
run.logs.push({
|
|
865
|
+
at: new Date().toISOString(),
|
|
866
|
+
kind: "event",
|
|
867
|
+
message: "session.stop_requested",
|
|
868
|
+
data: { source: "web" },
|
|
869
|
+
});
|
|
870
|
+
run.updatedAt = new Date().toISOString();
|
|
871
|
+
db.upsertSession(run);
|
|
872
|
+
await sessionStore(req.params.sessionId).appendEvent("session.stop_requested", { source: "web" }).catch(() => {});
|
|
873
|
+
res.json({ ok: true, sessionId: req.params.sessionId });
|
|
874
|
+
});
|
|
875
|
+
|
|
849
876
|
app.get("/health", (_req, res) => {
|
|
850
877
|
res.json({ ok: true, port });
|
|
851
878
|
});
|