@tetsuo-ai/agenc 0.2.0 → 0.3.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/bin/agenc +4 -0
- package/generated/.gitkeep +0 -1
- package/generated/agenc-runtime-manifest.json +52 -14
- package/lib/runtime-manager.mjs +169 -0
- package/package.json +13 -21
- package/scripts/build-runtime-tarball.mjs +201 -0
- package/scripts/gen-manifest.mjs +91 -0
- package/scripts/postinstall.mjs +19 -0
- package/src/launcher.mjs +246 -0
- package/README.md +0 -144
- package/bin/agenc-runtime.js +0 -5
- package/bin/agenc.js +0 -5
- package/generated/agenc-runtime-manifest.json.sig +0 -1
- package/generated/agenc-runtime-public-key.pem +0 -3
- package/generated/agenc-runtime-trust-policy.json +0 -9
- package/lib/cli.js +0 -261
- package/lib/runtime-manager.js +0 -1111
- package/scripts/postinstall.js +0 -6
package/src/launcher.mjs
ADDED
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import { spawn } from "node:child_process";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { dirname, join, resolve } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
const DEFAULT_READY_TIMEOUT_MS = 2000;
|
|
9
|
+
const DEFAULT_POLL_MS = 25;
|
|
10
|
+
const READY_TIMEOUT_ENV = "AGENC_DAEMON_READY_TIMEOUT_MS";
|
|
11
|
+
const AUTOSTART_ENV = "AGENC_DAEMON_AUTOSTART";
|
|
12
|
+
|
|
13
|
+
const requireFromLauncher = createRequire(import.meta.url);
|
|
14
|
+
|
|
15
|
+
export function shouldAutostartDaemon(env = process.env) {
|
|
16
|
+
const raw = env[AUTOSTART_ENV]?.trim().toLowerCase();
|
|
17
|
+
if (raw === undefined || raw.length === 0) return true;
|
|
18
|
+
return raw !== "0" && raw !== "false" && raw !== "off";
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function resolveReadyTimeoutMs(env = process.env) {
|
|
22
|
+
const raw = env[READY_TIMEOUT_ENV]?.trim();
|
|
23
|
+
if (raw === undefined || raw.length === 0) return DEFAULT_READY_TIMEOUT_MS;
|
|
24
|
+
const parsed = Number.parseInt(raw, 10);
|
|
25
|
+
if (!Number.isInteger(parsed) || String(parsed) !== raw || parsed <= 0) {
|
|
26
|
+
throw new Error(`${READY_TIMEOUT_ENV} must be a positive integer`);
|
|
27
|
+
}
|
|
28
|
+
return parsed;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function resolveAgenCHome(env = process.env, userHome = homedir()) {
|
|
32
|
+
const configured = env.AGENC_HOME?.trim();
|
|
33
|
+
return configured && configured.length > 0
|
|
34
|
+
? configured
|
|
35
|
+
: join(userHome, ".agenc");
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function resolveDaemonPidPath(env = process.env, userHome = homedir()) {
|
|
39
|
+
return join(resolveAgenCHome(env, userHome), "daemon.pid");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function resolveDaemonCookiePath(env = process.env, userHome = homedir()) {
|
|
43
|
+
return join(resolveAgenCHome(env, userHome), "daemon.cookie");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export async function readDaemonPid(pidPath, readText = readFile) {
|
|
47
|
+
try {
|
|
48
|
+
const raw = (await readText(pidPath, "utf8")).trim();
|
|
49
|
+
if (!/^\d+$/.test(raw)) return null;
|
|
50
|
+
const pid = Number.parseInt(raw, 10);
|
|
51
|
+
return pid > 0 ? pid : null;
|
|
52
|
+
} catch (error) {
|
|
53
|
+
if (error?.code === "ENOENT") return null;
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function isPidRunning(pid, signalPid = process.kill) {
|
|
59
|
+
try {
|
|
60
|
+
signalPid(pid, 0);
|
|
61
|
+
return true;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
return error?.code === "EPERM";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function isDaemonCommand(argv) {
|
|
68
|
+
return argv[0] === "daemon";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Dev path: the `file:`-linked @tetsuo-ai/runtime resolves locally. Returns
|
|
72
|
+
// null in a published install where runtime is NOT an npm dependency (it's the
|
|
73
|
+
// downloaded GitHub-Releases artifact instead).
|
|
74
|
+
export function resolveRuntimeBin(requireFn = requireFromLauncher) {
|
|
75
|
+
try {
|
|
76
|
+
const runtimeEntry = requireFn.resolve("@tetsuo-ai/runtime");
|
|
77
|
+
return resolve(dirname(runtimeEntry), "../bin/agenc");
|
|
78
|
+
} catch (error) {
|
|
79
|
+
if (
|
|
80
|
+
error?.code === "MODULE_NOT_FOUND" ||
|
|
81
|
+
error?.code === "ERR_MODULE_NOT_FOUND"
|
|
82
|
+
) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
throw error;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Resolve the runtime entry for either mode: prefer the dev file:-link, else
|
|
90
|
+
// ensure (download + verify + extract) the published per-platform runtime.
|
|
91
|
+
export async function resolveRuntimeBinAsync({
|
|
92
|
+
requireFn = requireFromLauncher,
|
|
93
|
+
ensureFn,
|
|
94
|
+
} = {}) {
|
|
95
|
+
const dev = resolveRuntimeBin(requireFn);
|
|
96
|
+
if (dev !== null) return dev;
|
|
97
|
+
const ensureRuntime =
|
|
98
|
+
ensureFn ?? (await import("../lib/runtime-manager.mjs")).ensureRuntime;
|
|
99
|
+
return ensureRuntime();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export async function spawnNodeScript(
|
|
103
|
+
scriptPath,
|
|
104
|
+
args,
|
|
105
|
+
{
|
|
106
|
+
env = process.env,
|
|
107
|
+
cwd = process.cwd(),
|
|
108
|
+
stdio = "inherit",
|
|
109
|
+
spawnFn = spawn,
|
|
110
|
+
} = {},
|
|
111
|
+
) {
|
|
112
|
+
return new Promise((resolveExit, reject) => {
|
|
113
|
+
const child = spawnFn(process.execPath, [scriptPath, ...args], {
|
|
114
|
+
cwd,
|
|
115
|
+
env,
|
|
116
|
+
stdio,
|
|
117
|
+
});
|
|
118
|
+
child.once("error", reject);
|
|
119
|
+
child.once("exit", (code, signal) => {
|
|
120
|
+
if (signal !== null) {
|
|
121
|
+
reject(new Error(`agenc runtime exited from signal ${signal}`));
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
resolveExit(code ?? 1);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export async function spawnDaemon(runtimeBin, options = {}) {
|
|
130
|
+
const exitCode = await spawnNodeScript(runtimeBin, ["daemon", "start"], {
|
|
131
|
+
...options,
|
|
132
|
+
stdio: ["ignore", "ignore", "inherit"],
|
|
133
|
+
});
|
|
134
|
+
if (exitCode !== 0) {
|
|
135
|
+
throw new Error(`AgenC daemon start failed with exit code ${exitCode}`);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export async function isDaemonReady(
|
|
140
|
+
{
|
|
141
|
+
env = process.env,
|
|
142
|
+
userHome = homedir(),
|
|
143
|
+
readText = readFile,
|
|
144
|
+
signalPid = process.kill,
|
|
145
|
+
} = {},
|
|
146
|
+
) {
|
|
147
|
+
const pidPath = resolveDaemonPidPath(env, userHome);
|
|
148
|
+
const cookiePath = resolveDaemonCookiePath(env, userHome);
|
|
149
|
+
const pid = await readDaemonPid(pidPath, readText);
|
|
150
|
+
if (pid === null || !isPidRunning(pid, signalPid)) return false;
|
|
151
|
+
try {
|
|
152
|
+
return (await readText(cookiePath, "utf8")).trim().length > 0;
|
|
153
|
+
} catch (error) {
|
|
154
|
+
if (error?.code === "ENOENT") return false;
|
|
155
|
+
throw error;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export async function waitForDaemonReady(options = {}) {
|
|
160
|
+
const timeoutMs = options.timeoutMs ?? resolveReadyTimeoutMs(options.env);
|
|
161
|
+
const pollMs = options.pollMs ?? DEFAULT_POLL_MS;
|
|
162
|
+
const startedAt = Date.now();
|
|
163
|
+
const sleep = options.sleep ?? ((ms) => new Promise((done) => setTimeout(done, ms)));
|
|
164
|
+
|
|
165
|
+
while (Date.now() - startedAt < timeoutMs) {
|
|
166
|
+
if (await isDaemonReady(options)) return true;
|
|
167
|
+
await sleep(pollMs);
|
|
168
|
+
}
|
|
169
|
+
return isDaemonReady(options);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export async function ensureDaemonForLaunch({
|
|
173
|
+
argv = process.argv.slice(2),
|
|
174
|
+
env = process.env,
|
|
175
|
+
cwd = process.cwd(),
|
|
176
|
+
runtimeBin = resolveRuntimeBin(),
|
|
177
|
+
userHome = homedir(),
|
|
178
|
+
readText = readFile,
|
|
179
|
+
signalPid = process.kill,
|
|
180
|
+
spawnDaemonFn = spawnDaemon,
|
|
181
|
+
waitForReadyFn = waitForDaemonReady,
|
|
182
|
+
} = {}) {
|
|
183
|
+
if (isDaemonCommand(argv)) return { status: "skipped-daemon-command" };
|
|
184
|
+
if (!shouldAutostartDaemon(env)) return { status: "disabled" };
|
|
185
|
+
|
|
186
|
+
if (
|
|
187
|
+
await waitForReadyFn({
|
|
188
|
+
env,
|
|
189
|
+
userHome,
|
|
190
|
+
readText,
|
|
191
|
+
signalPid,
|
|
192
|
+
timeoutMs: 1,
|
|
193
|
+
pollMs: 1,
|
|
194
|
+
sleep: async () => {},
|
|
195
|
+
})
|
|
196
|
+
) {
|
|
197
|
+
return { status: "already-running" };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
await spawnDaemonFn(runtimeBin, { env, cwd });
|
|
201
|
+
const ready = await waitForReadyFn({ env, userHome, readText, signalPid });
|
|
202
|
+
if (!ready) {
|
|
203
|
+
throw new Error("AgenC daemon did not become ready before timeout");
|
|
204
|
+
}
|
|
205
|
+
return { status: "started" };
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
export async function main(
|
|
209
|
+
argv = process.argv.slice(2),
|
|
210
|
+
{
|
|
211
|
+
env = process.env,
|
|
212
|
+
cwd = process.cwd(),
|
|
213
|
+
runtimeBin,
|
|
214
|
+
userHome = homedir(),
|
|
215
|
+
} = {},
|
|
216
|
+
) {
|
|
217
|
+
// Resolve (and, in a published install, download + verify) the runtime before
|
|
218
|
+
// anything tries to spawn it. Sync default is avoided: it returns null when
|
|
219
|
+
// runtime isn't an npm dep, which is the normal published case.
|
|
220
|
+
let resolvedBin = runtimeBin;
|
|
221
|
+
try {
|
|
222
|
+
resolvedBin ??= await resolveRuntimeBinAsync();
|
|
223
|
+
} catch (error) {
|
|
224
|
+
process.stderr.write(
|
|
225
|
+
`agenc: could not obtain runtime: ${
|
|
226
|
+
error instanceof Error ? error.message : String(error)
|
|
227
|
+
}\n`,
|
|
228
|
+
);
|
|
229
|
+
return 1;
|
|
230
|
+
}
|
|
231
|
+
try {
|
|
232
|
+
await ensureDaemonForLaunch({ argv, env, cwd, runtimeBin: resolvedBin, userHome });
|
|
233
|
+
} catch (error) {
|
|
234
|
+
process.stderr.write(
|
|
235
|
+
`agenc: daemon autostart failed: ${
|
|
236
|
+
error instanceof Error ? error.message : String(error)
|
|
237
|
+
}\n`,
|
|
238
|
+
);
|
|
239
|
+
return 1;
|
|
240
|
+
}
|
|
241
|
+
return spawnNodeScript(resolvedBin, argv, { env, cwd, stdio: "inherit" });
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
245
|
+
process.exitCode = await main();
|
|
246
|
+
}
|
package/README.md
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
# agenc
|
|
2
|
-
|
|
3
|
-
Public CLI and launcher for the AgenC framework.
|
|
4
|
-
|
|
5
|
-
This package owns the user-facing global install surface:
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
npm install -g @tetsuo-ai/agenc
|
|
9
|
-
agenc onboard
|
|
10
|
-
agenc start
|
|
11
|
-
agenc
|
|
12
|
-
agenc ui
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
`agenc onboard` is the canonical first-run path. It opens an interactive
|
|
16
|
-
terminal onboarding flow that:
|
|
17
|
-
|
|
18
|
-
- validates an xAI API key
|
|
19
|
-
- lets the operator shape the agent name, mission, role, soul, and tool posture
|
|
20
|
-
- writes the canonical config at `~/.agenc/config.json`
|
|
21
|
-
- generates the curated workspace markdown profile under `~/.agenc/workspace/`
|
|
22
|
-
|
|
23
|
-
It does not expose the runtime source tree directly. Instead, it installs and
|
|
24
|
-
launches the matching AgenC runtime artifact for the current supported
|
|
25
|
-
platform.
|
|
26
|
-
|
|
27
|
-
The supported npm install identity is `@tetsuo-ai/agenc`. The unscoped
|
|
28
|
-
`agenc` package name is not part of the supported public release contract.
|
|
29
|
-
|
|
30
|
-
Current public support is intentionally narrow:
|
|
31
|
-
|
|
32
|
-
- Linux `x64`
|
|
33
|
-
- Node `>=18.0.0`
|
|
34
|
-
|
|
35
|
-
Current validated release-gate lanes:
|
|
36
|
-
|
|
37
|
-
- Node `18` minimum floor
|
|
38
|
-
- Node `20` mainline lane
|
|
39
|
-
|
|
40
|
-
Production release channel:
|
|
41
|
-
|
|
42
|
-
- npm package: `@tetsuo-ai/agenc`
|
|
43
|
-
- runtime artifact host: GitHub Releases on `tetsuo-ai/agenc-core`
|
|
44
|
-
- trust: embedded signed manifest + embedded public key + embedded trust policy
|
|
45
|
-
|
|
46
|
-
After the matching runtime artifact is installed, `agenc` can continue to run
|
|
47
|
-
offline against the local install. The npm package name is scoped, but the CLI
|
|
48
|
-
binary remains `agenc`.
|
|
49
|
-
|
|
50
|
-
`agenc` exposes two primary local operator surfaces against the same daemon:
|
|
51
|
-
|
|
52
|
-
- `agenc` attaches the terminal operator console
|
|
53
|
-
- `agenc ui` opens or prints the local dashboard URL on `/ui/`
|
|
54
|
-
|
|
55
|
-
For automation or remote shells, use:
|
|
56
|
-
|
|
57
|
-
```bash
|
|
58
|
-
agenc ui --no-open
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
## First-party connector lifecycle
|
|
62
|
-
|
|
63
|
-
The first V1 connector is Telegram. It is managed through the same daemon as the
|
|
64
|
-
CLI and dashboard; there is no separate connector service to start.
|
|
65
|
-
|
|
66
|
-
```bash
|
|
67
|
-
agenc connector list
|
|
68
|
-
agenc connector status telegram
|
|
69
|
-
agenc connector add telegram --bot-token-env TELEGRAM_BOT_TOKEN
|
|
70
|
-
agenc connector remove telegram
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
For non-interactive shells or secret managers, you can pipe the bot token on
|
|
74
|
-
stdin instead:
|
|
75
|
-
|
|
76
|
-
```bash
|
|
77
|
-
printf '%s' "$TELEGRAM_BOT_TOKEN" | agenc connector add telegram --bot-token-stdin
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
Connector health and pending-restart state are exposed through both:
|
|
81
|
-
|
|
82
|
-
- `agenc connector status telegram`
|
|
83
|
-
- `agenc ui` on the dashboard status view
|
|
84
|
-
|
|
85
|
-
## Wrapper-local runtime management
|
|
86
|
-
|
|
87
|
-
```bash
|
|
88
|
-
agenc runtime where
|
|
89
|
-
agenc runtime install
|
|
90
|
-
agenc runtime update
|
|
91
|
-
agenc runtime uninstall
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
## First-use devnet marketplace rehearsal
|
|
95
|
-
|
|
96
|
-
For public release-path docs and operator rehearsal, use the top-level `agenc`
|
|
97
|
-
commands. `agenc-runtime` remains available as a compatibility alias after the
|
|
98
|
-
runtime is installed, but it is not the primary public wrapper path.
|
|
99
|
-
|
|
100
|
-
Supported release-path boundary:
|
|
101
|
-
|
|
102
|
-
- Linux `x64`
|
|
103
|
-
- Node `>=18.0.0`
|
|
104
|
-
|
|
105
|
-
Manual prerequisites that still live outside the wrapper:
|
|
106
|
-
|
|
107
|
-
- Solana CLI available on `PATH`
|
|
108
|
-
- funded devnet keypair at `SOLANA_KEYPAIR_PATH` or `~/.config/solana/id.json`
|
|
109
|
-
- `--rpc` or `AGENC_RUNTIME_RPC_URL`
|
|
110
|
-
- optional `--program-id` or `AGENC_RUNTIME_PROGRAM_ID` when testing a non-default deployment
|
|
111
|
-
- a second funded signer plus a second agent registration if you want a separate worker identity for `claim` and `complete`
|
|
112
|
-
|
|
113
|
-
Minimal creator flow:
|
|
114
|
-
|
|
115
|
-
```bash
|
|
116
|
-
export AGENC_RUNTIME_RPC_URL=https://api.devnet.solana.com
|
|
117
|
-
agenc onboard
|
|
118
|
-
agenc runtime install
|
|
119
|
-
agenc start
|
|
120
|
-
agenc agent register --rpc "$AGENC_RUNTIME_RPC_URL"
|
|
121
|
-
agenc market tasks create --description "public task" --reward 50000000 --rpc "$AGENC_RUNTIME_RPC_URL"
|
|
122
|
-
agenc market tasks list --rpc "$AGENC_RUNTIME_RPC_URL"
|
|
123
|
-
agenc market tui --rpc "$AGENC_RUNTIME_RPC_URL"
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
Claim and complete from a worker signer:
|
|
127
|
-
|
|
128
|
-
```bash
|
|
129
|
-
export SOLANA_KEYPAIR_PATH=/path/to/worker.json
|
|
130
|
-
agenc agent register --rpc "$AGENC_RUNTIME_RPC_URL"
|
|
131
|
-
agenc market tasks claim <taskPda> --rpc "$AGENC_RUNTIME_RPC_URL"
|
|
132
|
-
agenc market tasks complete <taskPda> --result-data "completed via public wrapper" --rpc "$AGENC_RUNTIME_RPC_URL"
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
Notes:
|
|
136
|
-
|
|
137
|
-
- `tasks claim|complete|dispute` require the signer wallet to already control a registered agent.
|
|
138
|
-
- `disputes resolve` is not part of the public first-use rehearsal; it requires the protocol authority wallet.
|
|
139
|
-
|
|
140
|
-
## Development
|
|
141
|
-
|
|
142
|
-
The embedded runtime manifest in `generated/` is produced by the `agenc-core`
|
|
143
|
-
artifact preparation scripts. Local smoke tests use the same packaged manifest
|
|
144
|
-
flow as publish/release preparation.
|
package/bin/agenc-runtime.js
DELETED
package/bin/agenc.js
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
nyIC/iBXuyr5jy7GThy5GsYjD9GgmmnSnbD54CpupYwr97qdAhmRuxrA0cqxOegFwN1pAu6Lu+qOYHyaePBEAw==
|
package/lib/cli.js
DELETED
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
import process from "node:process";
|
|
2
|
-
import {
|
|
3
|
-
describeRuntimeInstall,
|
|
4
|
-
ensureRuntimeFromSource,
|
|
5
|
-
ensureRuntimeInstalled,
|
|
6
|
-
RuntimeInstallError,
|
|
7
|
-
spawnInstalledRuntimeBin,
|
|
8
|
-
spawnInstalledRuntimeBinFromSource,
|
|
9
|
-
uninstallRuntime,
|
|
10
|
-
} from "./runtime-manager.js";
|
|
11
|
-
|
|
12
|
-
function writeLine(stream, value) {
|
|
13
|
-
stream.write(`${value}\n`);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function buildAgencHelp() {
|
|
17
|
-
return [
|
|
18
|
-
"agenc [runtime-command] [options]",
|
|
19
|
-
"agenc runtime <where|install|update|uninstall>",
|
|
20
|
-
"",
|
|
21
|
-
"Examples:",
|
|
22
|
-
" agenc onboard",
|
|
23
|
-
" agenc ui",
|
|
24
|
-
" agenc start",
|
|
25
|
-
" agenc status",
|
|
26
|
-
" agenc logs",
|
|
27
|
-
" agenc runtime where",
|
|
28
|
-
"",
|
|
29
|
-
"Marketplace / devnet examples:",
|
|
30
|
-
" agenc agent register --rpc https://api.devnet.solana.com",
|
|
31
|
-
" agenc market tasks create --description 'public task' --reward 50000000 --rpc https://api.devnet.solana.com",
|
|
32
|
-
" agenc market tui",
|
|
33
|
-
"",
|
|
34
|
-
"Supported public wrapper tuple:",
|
|
35
|
-
" Linux x64, Node >=18.0.0",
|
|
36
|
-
"",
|
|
37
|
-
"Compatibility alias:",
|
|
38
|
-
" agenc-runtime remains available after install, but public docs should use agenc.",
|
|
39
|
-
"",
|
|
40
|
-
"Dev / from-source:",
|
|
41
|
-
" agenc start --from-source",
|
|
42
|
-
" agenc start --from-source --source-dir /path/to/agenc-core/runtime",
|
|
43
|
-
" Builds runtime/dist locally and points ~/.agenc/runtime/current at it.",
|
|
44
|
-
].join("\n");
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function buildAgencRuntimeHelp() {
|
|
48
|
-
return [
|
|
49
|
-
"agenc-runtime [runtime-command] [options]",
|
|
50
|
-
"",
|
|
51
|
-
"Compatibility alias for the installed AgenC runtime CLI.",
|
|
52
|
-
"",
|
|
53
|
-
"Dev flags:",
|
|
54
|
-
" --from-source build the local agenc-core/runtime tree",
|
|
55
|
-
" and run the daemon from there",
|
|
56
|
-
" --source-dir <path> explicit runtime/ source path",
|
|
57
|
-
].join("\n");
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async function runRuntimeAdmin(argv, io, deps, runtimeOptions) {
|
|
61
|
-
const command = argv[0];
|
|
62
|
-
const force = argv.includes("--force");
|
|
63
|
-
if (!command || command === "help" || command === "--help" || command === "-h") {
|
|
64
|
-
writeLine(
|
|
65
|
-
io.stdout,
|
|
66
|
-
"agenc runtime <where|install|update|uninstall> [--force]",
|
|
67
|
-
);
|
|
68
|
-
return 0;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (command === "where") {
|
|
72
|
-
const description = await deps.describeRuntimeInstall(runtimeOptions);
|
|
73
|
-
writeLine(
|
|
74
|
-
io.stdout,
|
|
75
|
-
JSON.stringify(
|
|
76
|
-
{
|
|
77
|
-
runtimeHome: description.runtimeHome,
|
|
78
|
-
installed: description.installed,
|
|
79
|
-
releaseDir: description.releaseDir,
|
|
80
|
-
currentDir: description.currentDir,
|
|
81
|
-
manifestSource: description.manifestSource,
|
|
82
|
-
manifestDigest: description.manifestDigest,
|
|
83
|
-
trustPolicy: description.trustPolicy
|
|
84
|
-
? {
|
|
85
|
-
wrapperVersion: description.trustPolicy.wrapperVersion,
|
|
86
|
-
keyId: description.trustPolicy.keyId,
|
|
87
|
-
releaseChannel: description.trustPolicy.releaseChannel ?? null,
|
|
88
|
-
releaseRepository:
|
|
89
|
-
description.trustPolicy.releaseRepository ?? null,
|
|
90
|
-
releaseTag: description.trustPolicy.releaseTag ?? null,
|
|
91
|
-
revokedManifestDigests:
|
|
92
|
-
description.trustPolicy.revokedManifestDigests,
|
|
93
|
-
revokedRuntimeVersions:
|
|
94
|
-
description.trustPolicy.revokedRuntimeVersions,
|
|
95
|
-
}
|
|
96
|
-
: null,
|
|
97
|
-
selectedArtifact: description.selectedArtifact
|
|
98
|
-
? {
|
|
99
|
-
runtimeVersion: description.selectedArtifact.runtimeVersion,
|
|
100
|
-
platform: description.selectedArtifact.platform,
|
|
101
|
-
arch: description.selectedArtifact.arch,
|
|
102
|
-
nodeRange: description.selectedArtifact.nodeRange,
|
|
103
|
-
}
|
|
104
|
-
: null,
|
|
105
|
-
},
|
|
106
|
-
null,
|
|
107
|
-
2,
|
|
108
|
-
),
|
|
109
|
-
);
|
|
110
|
-
return 0;
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (command === "install" || command === "update") {
|
|
114
|
-
const installation = await deps.ensureRuntimeInstalled({
|
|
115
|
-
...runtimeOptions,
|
|
116
|
-
force: command === "update",
|
|
117
|
-
});
|
|
118
|
-
writeLine(
|
|
119
|
-
io.stdout,
|
|
120
|
-
`Installed AgenC runtime ${installation.selectedArtifact.runtimeVersion} at ${installation.releaseDir}`,
|
|
121
|
-
);
|
|
122
|
-
return 0;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (command === "uninstall") {
|
|
126
|
-
const result = await deps.uninstallRuntime({ ...runtimeOptions, force });
|
|
127
|
-
writeLine(
|
|
128
|
-
io.stdout,
|
|
129
|
-
result.removed
|
|
130
|
-
? `Removed AgenC runtime at ${result.releaseDir}\nPreserved: ${result.preservedPaths.join(", ")}`
|
|
131
|
-
: "No installed AgenC runtime found for the current platform",
|
|
132
|
-
);
|
|
133
|
-
return 0;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
writeLine(io.stderr, `Unknown agenc runtime command: ${command}`);
|
|
137
|
-
return 2;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const DEFAULT_DEPS = {
|
|
141
|
-
ensureRuntimeInstalled,
|
|
142
|
-
ensureRuntimeFromSource,
|
|
143
|
-
describeRuntimeInstall,
|
|
144
|
-
uninstallRuntime,
|
|
145
|
-
spawnInstalledRuntimeBin,
|
|
146
|
-
spawnInstalledRuntimeBinFromSource,
|
|
147
|
-
};
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Cut 6.1: detect and strip the `--from-source` flag (and the optional
|
|
151
|
-
* `--source-dir <path>`) from the wrapper argv. Returns the remaining argv
|
|
152
|
-
* plus the parsed flag values so the rest of the dispatcher can continue
|
|
153
|
-
* to operate on a clean argument list.
|
|
154
|
-
*/
|
|
155
|
-
function extractFromSourceFlags(argv) {
|
|
156
|
-
const remaining = [];
|
|
157
|
-
let fromSource = false;
|
|
158
|
-
let sourceDir;
|
|
159
|
-
for (let i = 0; i < argv.length; i++) {
|
|
160
|
-
const arg = argv[i];
|
|
161
|
-
if (arg === "--from-source") {
|
|
162
|
-
fromSource = true;
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
if (arg === "--source-dir" && i + 1 < argv.length) {
|
|
166
|
-
sourceDir = argv[++i];
|
|
167
|
-
fromSource = true;
|
|
168
|
-
continue;
|
|
169
|
-
}
|
|
170
|
-
if (typeof arg === "string" && arg.startsWith("--source-dir=")) {
|
|
171
|
-
sourceDir = arg.slice("--source-dir=".length);
|
|
172
|
-
fromSource = true;
|
|
173
|
-
continue;
|
|
174
|
-
}
|
|
175
|
-
remaining.push(arg);
|
|
176
|
-
}
|
|
177
|
-
return { argv: remaining, fromSource, sourceDir };
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function formatRuntimeInstallError(error) {
|
|
181
|
-
if (error instanceof RuntimeInstallError) {
|
|
182
|
-
return error.message;
|
|
183
|
-
}
|
|
184
|
-
return error instanceof Error ? error.message : String(error);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
export async function runAgencWrapper(options = {}, deps = DEFAULT_DEPS) {
|
|
188
|
-
const rawArgv = options.argv ?? process.argv.slice(2);
|
|
189
|
-
const { argv, fromSource, sourceDir } = extractFromSourceFlags(rawArgv);
|
|
190
|
-
const stdout = options.stdout ?? process.stdout;
|
|
191
|
-
const stderr = options.stderr ?? process.stderr;
|
|
192
|
-
const runtimeOptions = {
|
|
193
|
-
env: options.env ?? process.env,
|
|
194
|
-
cwd: options.cwd ?? process.cwd(),
|
|
195
|
-
homeDir: options.homeDir,
|
|
196
|
-
packageRoot: options.packageRoot,
|
|
197
|
-
sourceDir,
|
|
198
|
-
};
|
|
199
|
-
|
|
200
|
-
try {
|
|
201
|
-
if (argv[0] === "runtime") {
|
|
202
|
-
return await runRuntimeAdmin(
|
|
203
|
-
argv.slice(1),
|
|
204
|
-
{ stdout, stderr },
|
|
205
|
-
deps,
|
|
206
|
-
runtimeOptions,
|
|
207
|
-
);
|
|
208
|
-
}
|
|
209
|
-
if (argv[0] === "--help" || argv[0] === "-h" || argv[0] === "help") {
|
|
210
|
-
writeLine(stdout, buildAgencHelp());
|
|
211
|
-
return 0;
|
|
212
|
-
}
|
|
213
|
-
if (fromSource) {
|
|
214
|
-
return await deps.spawnInstalledRuntimeBinFromSource(
|
|
215
|
-
"agenc",
|
|
216
|
-
argv,
|
|
217
|
-
runtimeOptions,
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
return await deps.spawnInstalledRuntimeBin("agenc", argv, runtimeOptions);
|
|
221
|
-
} catch (error) {
|
|
222
|
-
writeLine(stderr, formatRuntimeInstallError(error));
|
|
223
|
-
return 1;
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
export async function runAgencRuntimeWrapper(options = {}, deps = DEFAULT_DEPS) {
|
|
228
|
-
const rawArgv = options.argv ?? process.argv.slice(2);
|
|
229
|
-
const { argv, fromSource, sourceDir } = extractFromSourceFlags(rawArgv);
|
|
230
|
-
const stdout = options.stdout ?? process.stdout;
|
|
231
|
-
const stderr = options.stderr ?? process.stderr;
|
|
232
|
-
const runtimeOptions = {
|
|
233
|
-
env: options.env ?? process.env,
|
|
234
|
-
cwd: options.cwd ?? process.cwd(),
|
|
235
|
-
homeDir: options.homeDir,
|
|
236
|
-
packageRoot: options.packageRoot,
|
|
237
|
-
sourceDir,
|
|
238
|
-
};
|
|
239
|
-
|
|
240
|
-
try {
|
|
241
|
-
if (argv[0] === "--help" || argv[0] === "-h" || argv[0] === "help") {
|
|
242
|
-
writeLine(stdout, buildAgencRuntimeHelp());
|
|
243
|
-
return 0;
|
|
244
|
-
}
|
|
245
|
-
if (fromSource) {
|
|
246
|
-
return await deps.spawnInstalledRuntimeBinFromSource(
|
|
247
|
-
"agenc-runtime",
|
|
248
|
-
argv,
|
|
249
|
-
runtimeOptions,
|
|
250
|
-
);
|
|
251
|
-
}
|
|
252
|
-
return await deps.spawnInstalledRuntimeBin(
|
|
253
|
-
"agenc-runtime",
|
|
254
|
-
argv,
|
|
255
|
-
runtimeOptions,
|
|
256
|
-
);
|
|
257
|
-
} catch (error) {
|
|
258
|
-
writeLine(stderr, formatRuntimeInstallError(error));
|
|
259
|
-
return 1;
|
|
260
|
-
}
|
|
261
|
-
}
|