@mithran-hq/aegis 0.9.7 → 0.9.10
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 +16 -18
- package/aegis.mjs +103 -10
- package/package.json +2 -5
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ candidate and `latest` still names the previous accepted build. Install a pinned
|
|
|
27
27
|
version when you need a fixed build:
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
npm install -g @mithran-hq/aegis@0.9.
|
|
30
|
+
npm install -g @mithran-hq/aegis@0.9.10
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
Check the installed version:
|
|
@@ -43,9 +43,14 @@ aegis auth login
|
|
|
43
43
|
aegis auth status
|
|
44
44
|
```
|
|
45
45
|
|
|
46
|
-
`aegis auth login`
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
`aegis auth login` uses the issuer's RFC 8628 device authorization: it prints
|
|
47
|
+
a one-time code and a verification URL, then waits for approval. In human
|
|
48
|
+
output the code and URL go to stderr and the URL is opened in the browser
|
|
49
|
+
unless `--no-browser` is given; success goes to stdout. Pass `--no-browser` to
|
|
50
|
+
open the URL yourself on any device. With `--json` the command is
|
|
51
|
+
noninteractive: the code event and the signed-in or error result go to stdout,
|
|
52
|
+
and it never opens a browser. An issuer without device sign-in is refused; no
|
|
53
|
+
localhost callback is used. Sign out with `aegis auth logout`.
|
|
49
54
|
|
|
50
55
|
## Gateway
|
|
51
56
|
|
|
@@ -85,27 +90,20 @@ account.
|
|
|
85
90
|
|
|
86
91
|
## Managed Forge skill
|
|
87
92
|
|
|
88
|
-
|
|
89
|
-
for Claude and Codex:
|
|
93
|
+
After installing, run `aegis skills install` to install the `aegis-forge`
|
|
94
|
+
instruction skill for Claude and Codex:
|
|
90
95
|
|
|
91
96
|
- `~/.claude/skills/aegis-forge/SKILL.md`
|
|
92
97
|
- `~/.agents/skills/aegis-forge/SKILL.md`
|
|
93
98
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
run `aegis skills install` to install explicitly.
|
|
98
|
-
|
|
99
|
-
If npm installs with scripts disabled, install the skill afterwards:
|
|
100
|
-
|
|
101
|
-
```bash
|
|
102
|
-
aegis skills install
|
|
103
|
-
```
|
|
99
|
+
The CLI prints a one-line reminder on first use until the skill is installed.
|
|
100
|
+
No npm lifecycle script is declared, so npm 11 imposes no script allowlist and
|
|
101
|
+
no `--allow-scripts` step is needed.
|
|
104
102
|
|
|
105
103
|
Check the installed state with `aegis skills status`. Existing files Aegis does
|
|
106
104
|
not own, changed Aegis-owned files and unexpected entries are preserved and
|
|
107
|
-
reported; unsafe paths are not followed.
|
|
108
|
-
|
|
105
|
+
reported; unsafe paths are not followed. The standard OS-account homes are
|
|
106
|
+
used by default. A custom agent home is selected only by an explicit
|
|
109
107
|
`aegis skills install --agent-home <path>`.
|
|
110
108
|
|
|
111
109
|
npm has no uninstall hook, so remove the skill before removing the CLI:
|
package/aegis.mjs
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createHash } from "node:crypto";
|
|
3
|
-
import { readFileSync, statSync, writeSync } from "node:fs";
|
|
3
|
+
import { existsSync, readFileSync, statSync, writeSync } from "node:fs";
|
|
4
4
|
import { createRequire } from "node:module";
|
|
5
5
|
import { dirname, join } from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { spawn } from "node:child_process";
|
|
8
|
-
import { release as osRelease } from "node:os";
|
|
8
|
+
import { homedir, release as osRelease, userInfo } from "node:os";
|
|
9
9
|
|
|
10
10
|
const MANIFEST_SCHEMA = "aegis.npm.platform.v1";
|
|
11
11
|
const CAPABILITY = "aegis:product:reduced-default";
|
|
@@ -15,18 +15,107 @@ const MINIMUM_MACOS_MAJOR = 15;
|
|
|
15
15
|
const MH_MAGIC_64 = 0xfeedfacf;
|
|
16
16
|
const CPU_TYPE_ARM64 = 0x0100000c;
|
|
17
17
|
const FORWARDED_SIGNALS = ["SIGHUP", "SIGINT", "SIGTERM", "SIGQUIT"];
|
|
18
|
-
|
|
18
|
+
// The conventional 128+signal exit status for a payload that died by a signal.
|
|
19
|
+
// The launcher must never re-deliver that signal to itself: a second core-dump
|
|
20
|
+
// signal from the wrapper is a crash surface of its own and can take the
|
|
21
|
+
// foreground job, and the shell's job control, down with it.
|
|
22
|
+
const SIGNAL_NUMBERS = {
|
|
23
|
+
SIGHUP: 1,
|
|
24
|
+
SIGINT: 2,
|
|
25
|
+
SIGQUIT: 3,
|
|
26
|
+
SIGILL: 4,
|
|
27
|
+
SIGABRT: 6,
|
|
28
|
+
SIGFPE: 8,
|
|
29
|
+
SIGKILL: 9,
|
|
30
|
+
// Darwin numbers SIGBUS 10 (Linux uses 7); this launcher refuses every
|
|
31
|
+
// non-Darwin host before spawning a payload.
|
|
32
|
+
SIGBUS: 10,
|
|
33
|
+
SIGSEGV: 11,
|
|
34
|
+
SIGPIPE: 13,
|
|
35
|
+
SIGALRM: 14,
|
|
36
|
+
SIGTERM: 15,
|
|
37
|
+
};
|
|
38
|
+
const QUIET_COMMANDS = new Set(["skills", "help", "--version", "--help", "-h", "-V"]);
|
|
39
|
+
const VALUE_FLAGS = new Set(["--agent-home", "--config", "--connection", "--state-dir"]);
|
|
40
|
+
const FORGE_SKILL_PATHS = [
|
|
41
|
+
[".claude", "skills", "aegis-forge", "SKILL.md"],
|
|
42
|
+
[".agents", "skills", "aegis-forge", "SKILL.md"],
|
|
43
|
+
];
|
|
19
44
|
|
|
20
45
|
function refuse(message) {
|
|
21
46
|
const text = Buffer.from(`aegis: ${message}\n`, "utf8");
|
|
22
47
|
try {
|
|
23
48
|
writeSync(2, text);
|
|
24
49
|
} catch {
|
|
25
|
-
|
|
50
|
+
// A missing stderr must not mask the refusal or change the exit status.
|
|
26
51
|
}
|
|
27
52
|
process.exit(1);
|
|
28
53
|
}
|
|
29
54
|
|
|
55
|
+
function warn(message) {
|
|
56
|
+
const text = Buffer.from(`aegis: ${message}\n`, "utf8");
|
|
57
|
+
try {
|
|
58
|
+
// Best effort only. Falling back to process.stderr.write() can emit an
|
|
59
|
+
// asynchronous error on a broken pipe and terminate the launcher.
|
|
60
|
+
writeSync(2, text);
|
|
61
|
+
} catch {
|
|
62
|
+
// A missing stderr must not change the launcher's exit status.
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function explicitAgentHome(argv) {
|
|
67
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
68
|
+
const arg = argv[index];
|
|
69
|
+
if (arg === "--agent-home") {
|
|
70
|
+
return argv[index + 1] ?? null;
|
|
71
|
+
}
|
|
72
|
+
if (arg.startsWith("--agent-home=")) {
|
|
73
|
+
return arg.slice("--agent-home=".length);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function firstCommand(argv) {
|
|
80
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
81
|
+
const arg = argv[index];
|
|
82
|
+
if (VALUE_FLAGS.has(arg)) {
|
|
83
|
+
index += 1;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (arg.startsWith("-")) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
return arg;
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function accountHome() {
|
|
95
|
+
try {
|
|
96
|
+
// The native CLI ignores HOME for its default skill homes; match it.
|
|
97
|
+
return userInfo().homedir;
|
|
98
|
+
} catch {
|
|
99
|
+
return homedir();
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function forgeSkillInstalled(argv) {
|
|
104
|
+
const home = explicitAgentHome(argv) ?? accountHome();
|
|
105
|
+
return FORGE_SKILL_PATHS.some((parts) => existsSync(join(home, ...parts)));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function reportsFirstRunHint(argv) {
|
|
109
|
+
if (argv.some((arg) => QUIET_COMMANDS.has(arg))) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
const command = firstCommand(argv);
|
|
113
|
+
if (command === null || QUIET_COMMANDS.has(command)) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
return !forgeSkillInstalled(argv);
|
|
117
|
+
}
|
|
118
|
+
|
|
30
119
|
function readJson(path, label) {
|
|
31
120
|
let bytes;
|
|
32
121
|
try {
|
|
@@ -198,7 +287,13 @@ if (
|
|
|
198
287
|
refuse("native payload is not a Darwin arm64 Mach-O binary");
|
|
199
288
|
}
|
|
200
289
|
|
|
201
|
-
const
|
|
290
|
+
const argv = process.argv.slice(2);
|
|
291
|
+
if (reportsFirstRunHint(argv)) {
|
|
292
|
+
warn(
|
|
293
|
+
"the Forge skill is not installed; run `aegis skills install` to install it for Claude and Codex.",
|
|
294
|
+
);
|
|
295
|
+
}
|
|
296
|
+
const child = spawn(payloadPath, argv, { stdio: "inherit" });
|
|
202
297
|
const forwarders = new Map();
|
|
203
298
|
for (const signal of FORWARDED_SIGNALS) {
|
|
204
299
|
const handler = () => {
|
|
@@ -222,11 +317,9 @@ child.on("exit", (code, signal) => {
|
|
|
222
317
|
for (const name of FORWARDED_SIGNALS) {
|
|
223
318
|
process.removeAllListeners(name);
|
|
224
319
|
}
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
// Fall through to the conventional 128+signal status below.
|
|
229
|
-
}
|
|
320
|
+
// Report the payload's fatal signal as a conventional exit status. Do not
|
|
321
|
+
// re-raise it: the wrapper must not die by a second core-dump signal, which
|
|
322
|
+
// is what a terminal sees as the foreground job (and its shell) crashing.
|
|
230
323
|
process.exitCode = 128 + (SIGNAL_NUMBERS[signal] ?? 0);
|
|
231
324
|
return;
|
|
232
325
|
}
|
package/package.json
CHANGED
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"license": "Apache-2.0",
|
|
29
29
|
"name": "@mithran-hq/aegis",
|
|
30
30
|
"optionalDependencies": {
|
|
31
|
-
"@mithran-hq/aegis-darwin-arm64": "0.9.
|
|
31
|
+
"@mithran-hq/aegis-darwin-arm64": "0.9.10"
|
|
32
32
|
},
|
|
33
33
|
"os": [
|
|
34
34
|
"darwin"
|
|
@@ -37,8 +37,5 @@
|
|
|
37
37
|
"type": "git",
|
|
38
38
|
"url": "git+https://github.com/mithran-hq/aegis.git"
|
|
39
39
|
},
|
|
40
|
-
"
|
|
41
|
-
"postinstall": "node aegis.mjs skills install --npm-postinstall"
|
|
42
|
-
},
|
|
43
|
-
"version": "0.9.7"
|
|
40
|
+
"version": "0.9.10"
|
|
44
41
|
}
|