@garuda-agent/garuda 0.0.1 → 0.15.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/CHANGELOG.md +77 -0
- package/LICENSE +202 -0
- package/NOTICE +4 -0
- package/README.md +1097 -0
- package/SECURITY.md +32 -0
- package/dist/acpCommand-FZ2P5JFO.js +153 -0
- package/dist/anthropic-NQDBCHJU.js +327 -0
- package/dist/chunk-23FXBNBB.js +46 -0
- package/dist/chunk-2S62ZNTC.js +320 -0
- package/dist/chunk-2TAXSFWN.js +49 -0
- package/dist/chunk-36GQIVGT.js +526 -0
- package/dist/chunk-3CEPHS4C.js +231 -0
- package/dist/chunk-3NJNVZTK.js +123 -0
- package/dist/chunk-42DSA4EA.js +116 -0
- package/dist/chunk-4JSUUDFQ.js +118 -0
- package/dist/chunk-4P26OKTP.js +100 -0
- package/dist/chunk-5DEWP3YX.js +139 -0
- package/dist/chunk-A2QAIRQL.js +45 -0
- package/dist/chunk-ATW7BNIG.js +31 -0
- package/dist/chunk-B25VL4RU.js +242 -0
- package/dist/chunk-EPKWDCV4.js +9 -0
- package/dist/chunk-EU7PA45O.js +324 -0
- package/dist/chunk-FARS4R67.js +206 -0
- package/dist/chunk-G2AX7MGS.js +36 -0
- package/dist/chunk-HRGX53VC.js +173 -0
- package/dist/chunk-HUIK3WXO.js +234 -0
- package/dist/chunk-INRZR772.js +73 -0
- package/dist/chunk-K3SIUOAG.js +138 -0
- package/dist/chunk-K7KK7U44.js +9358 -0
- package/dist/chunk-KKB5W47S.js +231 -0
- package/dist/chunk-N4I6YS3E.js +73 -0
- package/dist/chunk-NXJ4XLB5.js +19 -0
- package/dist/chunk-OUCV3FCT.js +205 -0
- package/dist/chunk-PQNT34LY.js +1247 -0
- package/dist/chunk-PZS3V6P3.js +46 -0
- package/dist/chunk-S77XDSD4.js +157 -0
- package/dist/chunk-SI2QJYCR.js +1341 -0
- package/dist/chunk-WMOLXTBU.js +61 -0
- package/dist/chunk-YFQJXRIJ.js +193 -0
- package/dist/chunk-YQTGMCU6.js +111 -0
- package/dist/chunk-ZAE25JM2.js +158 -0
- package/dist/cli/index.js +1203 -0
- package/dist/create-N4I7TIZE.js +223 -0
- package/dist/deadline-F5RJJ2QW.js +86 -0
- package/dist/detect-LARDSLLZ.js +10 -0
- package/dist/evalCommand-3SSFMZQD.js +2798 -0
- package/dist/export-HN3CT7AV.js +158 -0
- package/dist/inkChat-34BTFG2K.js +1193 -0
- package/dist/install-VRPBZY5H.js +17 -0
- package/dist/job-3NYDMP2M.js +31 -0
- package/dist/jobCommand-XJ5GULS5.js +30 -0
- package/dist/launchd-QVHQ3VNN.js +40 -0
- package/dist/lspCommand-XNTBHXLA.js +99 -0
- package/dist/manager-4PMSXT47.js +14 -0
- package/dist/manager-CSI76AWF.js +847 -0
- package/dist/nightCommand-CR6ZHYGP.js +247 -0
- package/dist/oauth-W6OK6XXO.js +23 -0
- package/dist/openaiCompatible-CJWVJP5M.js +501 -0
- package/dist/policy-EJPLEEXP.js +31 -0
- package/dist/proxy-XJAHCZB2.js +192 -0
- package/dist/remove-LE7Q7K46.js +59 -0
- package/dist/run-ML6GSPN4.js +888 -0
- package/dist/server-R3GNIC33.js +676 -0
- package/dist/servers-6YDGWOZ6.js +24 -0
- package/dist/text-J2GD647J.js +11 -0
- package/package.json +84 -3
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
defaultResolve
|
|
4
|
+
} from "./chunk-4P26OKTP.js";
|
|
5
|
+
import {
|
|
6
|
+
checkAddress,
|
|
7
|
+
isIpLiteral
|
|
8
|
+
} from "./chunk-ATW7BNIG.js";
|
|
9
|
+
|
|
10
|
+
// src/net/proxy.ts
|
|
11
|
+
import { mkdtempSync, rmSync } from "fs";
|
|
12
|
+
import { createServer, request as httpRequest } from "http";
|
|
13
|
+
import { connect } from "net";
|
|
14
|
+
import { tmpdir } from "os";
|
|
15
|
+
import { join } from "path";
|
|
16
|
+
var NetworkProxy = class {
|
|
17
|
+
constructor(options) {
|
|
18
|
+
this.options = options;
|
|
19
|
+
}
|
|
20
|
+
options;
|
|
21
|
+
servers = [];
|
|
22
|
+
sockets = /* @__PURE__ */ new Set();
|
|
23
|
+
blocked = [];
|
|
24
|
+
dir;
|
|
25
|
+
port = 0;
|
|
26
|
+
socketPath = "";
|
|
27
|
+
async start() {
|
|
28
|
+
this.dir = mkdtempSync(join(tmpdir(), "garuda-net-"));
|
|
29
|
+
this.socketPath = join(this.dir, "proxy.sock");
|
|
30
|
+
const tcp = this.server();
|
|
31
|
+
const unix = this.server();
|
|
32
|
+
await new Promise((resolve, reject) => {
|
|
33
|
+
tcp.once("error", reject);
|
|
34
|
+
tcp.listen(0, "127.0.0.1", () => resolve());
|
|
35
|
+
});
|
|
36
|
+
await new Promise((resolve, reject) => {
|
|
37
|
+
unix.once("error", reject);
|
|
38
|
+
unix.listen(this.socketPath, () => resolve());
|
|
39
|
+
});
|
|
40
|
+
const address = tcp.address();
|
|
41
|
+
this.port = typeof address === "object" && address !== null ? address.port : 0;
|
|
42
|
+
return { port: this.port, socketPath: this.socketPath };
|
|
43
|
+
}
|
|
44
|
+
/** The hosts blocked since the last call. */
|
|
45
|
+
takeBlocked() {
|
|
46
|
+
const out = this.blocked;
|
|
47
|
+
this.blocked = [];
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
async close() {
|
|
51
|
+
for (const socket of this.sockets) socket.destroy();
|
|
52
|
+
await Promise.all(
|
|
53
|
+
this.servers.map((s) => new Promise((resolve) => s.close(() => resolve())))
|
|
54
|
+
);
|
|
55
|
+
if (this.dir !== void 0) rmSync(this.dir, { recursive: true, force: true });
|
|
56
|
+
}
|
|
57
|
+
server() {
|
|
58
|
+
const server = createServer((req, res) => {
|
|
59
|
+
void this.forwardHttp(req, res);
|
|
60
|
+
});
|
|
61
|
+
server.on("connect", (req, client, head) => {
|
|
62
|
+
void this.tunnel(req, client, head);
|
|
63
|
+
});
|
|
64
|
+
server.on("connection", (socket) => {
|
|
65
|
+
this.sockets.add(socket);
|
|
66
|
+
socket.on("close", () => this.sockets.delete(socket));
|
|
67
|
+
});
|
|
68
|
+
this.servers.push(server);
|
|
69
|
+
return server;
|
|
70
|
+
}
|
|
71
|
+
/** https: CONNECT host:port, then a raw tunnel. */
|
|
72
|
+
async tunnel(req, client, head) {
|
|
73
|
+
client.on("error", () => {
|
|
74
|
+
});
|
|
75
|
+
const target = parseAuthority(req.url ?? "");
|
|
76
|
+
if (target === void 0) {
|
|
77
|
+
refuse(client, 400, "Garuda's proxy: a CONNECT request needs host:port.");
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const address = await this.check(target.host, target.port);
|
|
81
|
+
if (typeof address !== "string") {
|
|
82
|
+
refuse(client, 403, address.reason);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
const upstream = connect(target.port, address);
|
|
86
|
+
this.sockets.add(upstream);
|
|
87
|
+
upstream.on("close", () => this.sockets.delete(upstream));
|
|
88
|
+
upstream.on("error", () => client.destroy());
|
|
89
|
+
client.on("close", () => upstream.destroy());
|
|
90
|
+
upstream.once("connect", () => {
|
|
91
|
+
client.write("HTTP/1.1 200 Connection Established\r\n\r\n");
|
|
92
|
+
if (head.length > 0) upstream.write(head);
|
|
93
|
+
upstream.pipe(client);
|
|
94
|
+
client.pipe(upstream);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
/** http: a request with an absolute URL. */
|
|
98
|
+
async forwardHttp(req, res) {
|
|
99
|
+
let url;
|
|
100
|
+
try {
|
|
101
|
+
url = new URL(req.url ?? "");
|
|
102
|
+
} catch {
|
|
103
|
+
res.writeHead(400).end("Garuda's proxy: only absolute http URLs.\n");
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
if (url.protocol !== "http:") {
|
|
107
|
+
res.writeHead(400).end("Garuda's proxy: only http here; https goes through CONNECT.\n");
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
const port = url.port === "" ? 80 : Number(url.port);
|
|
111
|
+
const address = await this.check(url.hostname, port);
|
|
112
|
+
if (typeof address !== "string") {
|
|
113
|
+
res.writeHead(403, { "content-type": "text/plain" }).end(`${address.reason}
|
|
114
|
+
`);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
const headers = { ...req.headers };
|
|
118
|
+
for (const name of Object.keys(headers)) {
|
|
119
|
+
if (name.startsWith("proxy-")) delete headers[name];
|
|
120
|
+
}
|
|
121
|
+
const upstream = httpRequest(
|
|
122
|
+
{
|
|
123
|
+
host: address,
|
|
124
|
+
port,
|
|
125
|
+
method: req.method,
|
|
126
|
+
path: `${url.pathname}${url.search}`,
|
|
127
|
+
headers: { ...headers, host: url.host }
|
|
128
|
+
},
|
|
129
|
+
(answer) => {
|
|
130
|
+
res.writeHead(answer.statusCode ?? 502, answer.headers);
|
|
131
|
+
answer.pipe(res);
|
|
132
|
+
}
|
|
133
|
+
);
|
|
134
|
+
upstream.on("error", () => {
|
|
135
|
+
if (!res.headersSent) res.writeHead(502).end("Garuda's proxy: the host did not answer.\n");
|
|
136
|
+
else res.destroy();
|
|
137
|
+
});
|
|
138
|
+
req.pipe(upstream);
|
|
139
|
+
}
|
|
140
|
+
/** The checked address to connect to, or why not. */
|
|
141
|
+
async check(host, port) {
|
|
142
|
+
const block = (reason) => {
|
|
143
|
+
this.blocked.push({ host, port, reason });
|
|
144
|
+
return { reason };
|
|
145
|
+
};
|
|
146
|
+
const bare = host.replace(/^\[|\]$/g, "").toLowerCase();
|
|
147
|
+
if (isIpLiteral(bare)) {
|
|
148
|
+
return block(`Garuda's network allowlist: use a host name, not the address ${bare}.`);
|
|
149
|
+
}
|
|
150
|
+
const decision = await this.options.decide(bare, port).catch(() => ({ allowed: false, reason: "the check failed" }));
|
|
151
|
+
if (!decision.allowed) {
|
|
152
|
+
return block(
|
|
153
|
+
`Garuda's network allowlist blocked ${bare}:${port}${decision.reason === void 0 ? "" : `: ${decision.reason}`}.`
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
let addresses;
|
|
157
|
+
try {
|
|
158
|
+
addresses = await (this.options.resolve ?? defaultResolve)(bare);
|
|
159
|
+
} catch {
|
|
160
|
+
return block(`Garuda's proxy: ${bare} has no address.`);
|
|
161
|
+
}
|
|
162
|
+
const allowed = this.options.addressAllowed ?? ((a) => checkAddress(a, false).ok);
|
|
163
|
+
if (addresses.length === 0 || !addresses.every((a) => allowed(a.address))) {
|
|
164
|
+
return block(`Garuda's proxy: ${bare} does not resolve to public addresses only.`);
|
|
165
|
+
}
|
|
166
|
+
return addresses[0].address;
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
function parseAuthority(text) {
|
|
170
|
+
const m = /^(\[[^\]]+\]|[^:/]+):(\d{1,5})$/.exec(text);
|
|
171
|
+
if (m === null) return void 0;
|
|
172
|
+
const port = Number(m[2]);
|
|
173
|
+
if (port < 1 || port > 65535) return void 0;
|
|
174
|
+
return { host: m[1].replace(/^\[|\]$/g, ""), port };
|
|
175
|
+
}
|
|
176
|
+
function refuse(client, status, text) {
|
|
177
|
+
const body = `${text}
|
|
178
|
+
`;
|
|
179
|
+
client.end(
|
|
180
|
+
`HTTP/1.1 ${status} ${status === 403 ? "Forbidden" : "Bad Request"}\r
|
|
181
|
+
content-type: text/plain\r
|
|
182
|
+
content-length: ${Buffer.byteLength(body)}\r
|
|
183
|
+
connection: close\r
|
|
184
|
+
\r
|
|
185
|
+
${body}`
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
export {
|
|
189
|
+
NetworkProxy,
|
|
190
|
+
parseAuthority
|
|
191
|
+
};
|
|
192
|
+
//# sourceMappingURL=proxy-XJAHCZB2.js.map
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
git
|
|
4
|
+
} from "./chunk-2TAXSFWN.js";
|
|
5
|
+
import "./chunk-SI2QJYCR.js";
|
|
6
|
+
import "./chunk-WMOLXTBU.js";
|
|
7
|
+
import "./chunk-HUIK3WXO.js";
|
|
8
|
+
import {
|
|
9
|
+
JOBS_DIR,
|
|
10
|
+
jobPath
|
|
11
|
+
} from "./chunk-KKB5W47S.js";
|
|
12
|
+
import "./chunk-3NJNVZTK.js";
|
|
13
|
+
|
|
14
|
+
// src/jobs/remove.ts
|
|
15
|
+
import { existsSync } from "fs";
|
|
16
|
+
import { rm } from "fs/promises";
|
|
17
|
+
import { join } from "path";
|
|
18
|
+
async function jobLeftovers(executor, job) {
|
|
19
|
+
const files = [
|
|
20
|
+
jobPath(job.root, job.id),
|
|
21
|
+
...["md", "log"].map((e) => join(job.root, JOBS_DIR, `${job.id}.${e}`))
|
|
22
|
+
].filter((f) => existsSync(f));
|
|
23
|
+
const ref = await git(
|
|
24
|
+
executor,
|
|
25
|
+
job.root,
|
|
26
|
+
["rev-parse", "--verify", "--quiet", `refs/heads/${job.branch}`],
|
|
27
|
+
{
|
|
28
|
+
check: false
|
|
29
|
+
}
|
|
30
|
+
);
|
|
31
|
+
const branch = ref.exitCode === 0;
|
|
32
|
+
const merged = branch && (await git(executor, job.root, ["merge-base", "--is-ancestor", job.branch, "HEAD"], {
|
|
33
|
+
check: false
|
|
34
|
+
})).exitCode === 0;
|
|
35
|
+
return { files, worktree: existsSync(job.worktree), branch, merged };
|
|
36
|
+
}
|
|
37
|
+
async function removeJob(executor, job, left, withBranch) {
|
|
38
|
+
const gone = [];
|
|
39
|
+
if (left.worktree) {
|
|
40
|
+
await git(executor, job.root, ["worktree", "remove", "--force", job.worktree], {
|
|
41
|
+
check: false
|
|
42
|
+
});
|
|
43
|
+
await rm(job.worktree, { recursive: true, force: true });
|
|
44
|
+
gone.push("the worktree");
|
|
45
|
+
}
|
|
46
|
+
await git(executor, job.root, ["worktree", "prune"], { check: false });
|
|
47
|
+
if (withBranch && left.branch) {
|
|
48
|
+
await git(executor, job.root, ["branch", "-D", job.branch]);
|
|
49
|
+
gone.push(`the branch ${job.branch}`);
|
|
50
|
+
}
|
|
51
|
+
for (const file of left.files) await rm(file, { force: true });
|
|
52
|
+
if (left.files.length > 0) gone.unshift("the job file, report and log");
|
|
53
|
+
return gone;
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
jobLeftovers,
|
|
57
|
+
removeJob
|
|
58
|
+
};
|
|
59
|
+
//# sourceMappingURL=remove-LE7Q7K46.js.map
|