@aegisagentics/agent 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,275 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import crypto from "node:crypto";
3
+ import fs from "node:fs/promises";
4
+ import path from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ export const DEFAULT_RELEASE_BASE_URL = "https://aegisagentics.com/releases/aegis-agent";
8
+ const MANIFEST_FILE = path.resolve(
9
+ path.dirname(fileURLToPath(import.meta.url)),
10
+ "..",
11
+ "release-manifest.json"
12
+ );
13
+ const PACKAGE_FILE = path.resolve(
14
+ path.dirname(fileURLToPath(import.meta.url)),
15
+ "..",
16
+ "..",
17
+ "package.json"
18
+ );
19
+ const MAX_ARCHIVE_BYTES = 100 * 1024 * 1024;
20
+
21
+ export function releasePlatformKey({
22
+ platform = process.platform,
23
+ arch = process.arch
24
+ } = {}) {
25
+ const key = `${platform}-${arch}`;
26
+ switch (key) {
27
+ case "darwin-arm64":
28
+ case "darwin-x64":
29
+ case "linux-arm64":
30
+ case "linux-x64":
31
+ return key;
32
+ default:
33
+ throw new Error(
34
+ `Unsupported Aegis Agent platform: ${platform}/${arch}. Supported: darwin-arm64, darwin-x64, linux-arm64, linux-x64.`
35
+ );
36
+ }
37
+ }
38
+
39
+ export async function ensureNativeBinary({
40
+ binaryPath,
41
+ fetchImpl = globalThis.fetch,
42
+ force = process.env.AEGIS_AGENT_FORCE_BINARY_DOWNLOAD === "1",
43
+ platform = process.platform,
44
+ arch = process.arch,
45
+ releaseBaseUrl = process.env.AEGIS_AGENT_RELEASE_BASE_URL ?? DEFAULT_RELEASE_BASE_URL,
46
+ releaseVersion = process.env.AEGIS_AGENT_RELEASE_VERSION
47
+ } = {}) {
48
+ if (!binaryPath) {
49
+ throw new Error("binaryPath is required to install Aegis Agent runtime");
50
+ }
51
+ if (!force && (await exists(binaryPath))) {
52
+ return {
53
+ binaryPath,
54
+ downloaded: false
55
+ };
56
+ }
57
+
58
+ const version = releaseVersion ?? (await packageVersion());
59
+ const manifest = await loadReleaseManifest({
60
+ fetchImpl,
61
+ releaseBaseUrl,
62
+ version
63
+ });
64
+ const platformKey = releasePlatformKey({ platform, arch });
65
+ const artifact = resolveReleaseArtifact({
66
+ manifest,
67
+ platformKey,
68
+ releaseBaseUrl
69
+ });
70
+ await installReleaseArtifact({
71
+ artifact,
72
+ binaryPath,
73
+ fetchImpl
74
+ });
75
+ return {
76
+ artifact,
77
+ binaryPath,
78
+ downloaded: true
79
+ };
80
+ }
81
+
82
+ export async function loadReleaseManifest({
83
+ fetchImpl = globalThis.fetch,
84
+ releaseBaseUrl = DEFAULT_RELEASE_BASE_URL,
85
+ version
86
+ } = {}) {
87
+ const bundled = await loadBundledManifest();
88
+ if (bundled && hasArtifacts(bundled)) {
89
+ return bundled;
90
+ }
91
+ if (!version) {
92
+ throw new Error("release version is required when bundled manifest has no artifacts");
93
+ }
94
+ if (typeof fetchImpl !== "function") {
95
+ throw new Error("global fetch is unavailable; Node 18 or newer is required");
96
+ }
97
+ const manifestUrl = `${trimTrailingSlash(releaseBaseUrl)}/versions/${encodeURIComponent(
98
+ version
99
+ )}/manifest.json`;
100
+ const response = await fetchImpl(manifestUrl, {
101
+ headers: {
102
+ Accept: "application/json"
103
+ }
104
+ });
105
+ if (!response.ok) {
106
+ throw new Error(`Aegis Agent release manifest download failed: HTTP ${response.status}`);
107
+ }
108
+ return validateManifest(await response.json());
109
+ }
110
+
111
+ export function resolveReleaseArtifact({
112
+ manifest,
113
+ platformKey,
114
+ releaseBaseUrl = DEFAULT_RELEASE_BASE_URL
115
+ }) {
116
+ const validManifest = validateManifest(manifest);
117
+ const artifact = validManifest.artifacts[platformKey];
118
+ if (!artifact) {
119
+ throw new Error(
120
+ `Aegis Agent release ${validManifest.version} does not include ${platformKey}`
121
+ );
122
+ }
123
+ const sha256 = String(artifact.sha256 ?? "").toLowerCase();
124
+ if (!/^[a-f0-9]{64}$/.test(sha256)) {
125
+ throw new Error(`Aegis Agent release artifact ${platformKey} has invalid sha256`);
126
+ }
127
+
128
+ const url = artifact.url
129
+ ? new URL(String(artifact.url))
130
+ : new URL(String(artifact.path ?? ""), `${trimTrailingSlash(releaseBaseUrl)}/`);
131
+ assertReleaseUrl(url, releaseBaseUrl);
132
+
133
+ const sizeBytes = Number(artifact.sizeBytes ?? 0);
134
+ if (!Number.isSafeInteger(sizeBytes) || sizeBytes <= 0 || sizeBytes > MAX_ARCHIVE_BYTES) {
135
+ throw new Error(`Aegis Agent release artifact ${platformKey} has invalid sizeBytes`);
136
+ }
137
+
138
+ return {
139
+ platformKey,
140
+ sha256,
141
+ sizeBytes,
142
+ url: url.toString()
143
+ };
144
+ }
145
+
146
+ export async function installReleaseArtifact({
147
+ artifact,
148
+ binaryPath,
149
+ fetchImpl = globalThis.fetch
150
+ }) {
151
+ if (typeof fetchImpl !== "function") {
152
+ throw new Error("global fetch is unavailable; Node 18 or newer is required");
153
+ }
154
+ const parentDir = path.dirname(binaryPath);
155
+ await fs.mkdir(parentDir, { recursive: true });
156
+ const tempDir = await fs.mkdtemp(path.join(parentDir, ".aegis-agent-download-"));
157
+ const archivePath = path.join(tempDir, "aegis-agent.tar.gz");
158
+ try {
159
+ const response = await fetchImpl(artifact.url);
160
+ if (!response.ok) {
161
+ throw new Error(`Aegis Agent binary download failed: HTTP ${response.status}`);
162
+ }
163
+ const archive = Buffer.from(await response.arrayBuffer());
164
+ if (archive.length !== artifact.sizeBytes) {
165
+ throw new Error(
166
+ `Aegis Agent binary size mismatch: expected ${artifact.sizeBytes}, got ${archive.length}`
167
+ );
168
+ }
169
+ const actualSha256 = crypto.createHash("sha256").update(archive).digest("hex");
170
+ if (actualSha256 !== artifact.sha256) {
171
+ throw new Error(
172
+ `Aegis Agent binary checksum mismatch: expected ${artifact.sha256}, got ${actualSha256}`
173
+ );
174
+ }
175
+ await fs.writeFile(archivePath, archive, { mode: 0o600 });
176
+ const extractedBinary = await extractVerifiedArchive({
177
+ archivePath,
178
+ tempDir
179
+ });
180
+ await fs.chmod(extractedBinary, 0o755);
181
+ const tempBinary = path.join(parentDir, `.aegis-agent.${process.pid}.tmp`);
182
+ await fs.copyFile(extractedBinary, tempBinary);
183
+ await fs.chmod(tempBinary, 0o755);
184
+ await fs.rename(tempBinary, binaryPath);
185
+ } finally {
186
+ await fs.rm(tempDir, { force: true, recursive: true });
187
+ }
188
+ }
189
+
190
+ async function extractVerifiedArchive({ archivePath, tempDir }) {
191
+ const list = spawnSync("tar", ["-tzf", archivePath], {
192
+ encoding: "utf8",
193
+ stdio: ["ignore", "pipe", "pipe"]
194
+ });
195
+ if (list.status !== 0) {
196
+ throw new Error(`Aegis Agent archive listing failed: ${list.stderr.trim()}`);
197
+ }
198
+ const entries = list.stdout
199
+ .split("\n")
200
+ .map((entry) => entry.trim())
201
+ .filter(Boolean);
202
+ for (const entry of entries) {
203
+ if (entry.startsWith("/") || entry.split("/").includes("..")) {
204
+ throw new Error(`Aegis Agent archive contains unsafe path: ${entry}`);
205
+ }
206
+ }
207
+ if (!entries.includes("aegis-agent")) {
208
+ throw new Error("Aegis Agent archive must contain aegis-agent at archive root");
209
+ }
210
+
211
+ const extract = spawnSync("tar", ["-xzf", archivePath, "-C", tempDir, "aegis-agent"], {
212
+ encoding: "utf8",
213
+ stdio: ["ignore", "pipe", "pipe"]
214
+ });
215
+ if (extract.status !== 0) {
216
+ throw new Error(`Aegis Agent archive extraction failed: ${extract.stderr.trim()}`);
217
+ }
218
+ return path.join(tempDir, "aegis-agent");
219
+ }
220
+
221
+ async function loadBundledManifest() {
222
+ try {
223
+ return validateManifest(JSON.parse(await fs.readFile(MANIFEST_FILE, "utf8")));
224
+ } catch (error) {
225
+ if (error.code === "ENOENT") {
226
+ return null;
227
+ }
228
+ throw error;
229
+ }
230
+ }
231
+
232
+ async function packageVersion() {
233
+ const packageJson = JSON.parse(await fs.readFile(PACKAGE_FILE, "utf8"));
234
+ return packageJson.version;
235
+ }
236
+
237
+ function validateManifest(manifest) {
238
+ if (!manifest || typeof manifest !== "object" || Array.isArray(manifest)) {
239
+ throw new Error("Aegis Agent release manifest must be an object");
240
+ }
241
+ if (typeof manifest.version !== "string" || manifest.version.trim() === "") {
242
+ throw new Error("Aegis Agent release manifest requires version");
243
+ }
244
+ if (!manifest.artifacts || typeof manifest.artifacts !== "object") {
245
+ throw new Error("Aegis Agent release manifest requires artifacts");
246
+ }
247
+ return manifest;
248
+ }
249
+
250
+ function hasArtifacts(manifest) {
251
+ return Object.keys(manifest.artifacts ?? {}).length > 0;
252
+ }
253
+
254
+ function assertReleaseUrl(url, releaseBaseUrl) {
255
+ const base = new URL(`${trimTrailingSlash(releaseBaseUrl)}/`);
256
+ if (url.protocol !== "https:" && base.hostname !== "localhost") {
257
+ throw new Error("Aegis Agent release artifacts must use HTTPS");
258
+ }
259
+ if (url.origin !== base.origin || !url.pathname.startsWith(base.pathname)) {
260
+ throw new Error(`Aegis Agent release artifact URL is outside the release prefix: ${url}`);
261
+ }
262
+ }
263
+
264
+ function trimTrailingSlash(value) {
265
+ return String(value).replace(/\/+$/, "");
266
+ }
267
+
268
+ async function exists(file) {
269
+ try {
270
+ await fs.access(file);
271
+ return true;
272
+ } catch {
273
+ return false;
274
+ }
275
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "schemaVersion": "aegis.agent_release_manifest.v1",
3
+ "version": "0.1.0",
4
+ "releaseBaseUrl": "https://aegisagentics.com/releases/aegis-agent",
5
+ "createdAt": "2026-06-22T18:55:17.615Z",
6
+ "artifacts": {
7
+ "darwin-arm64": {
8
+ "path": "versions/0.1.0/darwin-arm64/aegis-agent.tar.gz",
9
+ "sha256": "c11ceb6011c01e9ea2090494b36bf59d98a51058324e338295f68fcf60664fd0",
10
+ "sizeBytes": 2155326
11
+ },
12
+ "darwin-x64": {
13
+ "path": "versions/0.1.0/darwin-x64/aegis-agent.tar.gz",
14
+ "sha256": "0d1e1c8f8584dcbe23b47d12712371daf5faf488b614e8699b22d80a7cd3ef35",
15
+ "sizeBytes": 2248676
16
+ },
17
+ "linux-x64": {
18
+ "path": "versions/0.1.0/linux-x64/aegis-agent.tar.gz",
19
+ "sha256": "82dffedef43a7fefeb431739536ae6d3f26bd82f20ea826a528ffcd2cdb69517",
20
+ "sizeBytes": 2356443
21
+ }
22
+ }
23
+ }
package/package.json ADDED
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@aegisagentics/agent",
3
+ "version": "0.1.0",
4
+ "description": "Aegis Agent local installer and hook runtime for Codex, Claude, Cursor, and Goose.",
5
+ "type": "module",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/AegisAgentics/aegis-plugin.git"
9
+ },
10
+ "homepage": "https://github.com/AegisAgentics/aegis-plugin#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/AegisAgentics/aegis-plugin/issues"
13
+ },
14
+ "bin": {
15
+ "aegis-agent": "./npm/install.js",
16
+ "aegis-agent-install": "./npm/install.js"
17
+ },
18
+ "files": [
19
+ "npm/install.js",
20
+ "npm/lib",
21
+ "npm/release-manifest.json",
22
+ "README.md"
23
+ ],
24
+ "scripts": {
25
+ "test": "node --test npm/test/*.test.js"
26
+ },
27
+ "engines": {
28
+ "node": ">=18"
29
+ },
30
+ "license": "UNLICENSED",
31
+ "publishConfig": {
32
+ "access": "public"
33
+ }
34
+ }