@openagentsinc/pylon 0.2.2 → 0.2.5
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 +80 -3
- package/package.json +1 -1
- package/src/cli.js +101 -0
- package/src/index.js +430 -0
package/README.md
CHANGED
|
@@ -17,6 +17,8 @@ npx @openagentsinc/pylon --version 0.1.16
|
|
|
17
17
|
npx @openagentsinc/pylon --no-launch
|
|
18
18
|
npx @openagentsinc/pylon --no-updates
|
|
19
19
|
npx @openagentsinc/pylon status --json
|
|
20
|
+
npx @openagentsinc/pylon --register-openagents --no-launch --json
|
|
21
|
+
npx @openagentsinc/pylon --register-openagents --setup-mdk-wallet --no-launch --json
|
|
20
22
|
npx @openagentsinc/pylon --download-curated-cache --model gemma-4-e2b --run-diagnostics
|
|
21
23
|
npx @openagentsinc/pylon --verbose
|
|
22
24
|
```
|
|
@@ -64,6 +66,11 @@ The launcher:
|
|
|
64
66
|
unless `--no-launch` is set
|
|
65
67
|
- forwards CLI subcommands such as `pylon status --json` to the installed
|
|
66
68
|
`pylon` binary after bootstrap instead of opening `pylon-tui`
|
|
69
|
+
- can register the local Pylon with OpenAgents and send a public-safe
|
|
70
|
+
heartbeat when `--register-openagents` is explicitly set
|
|
71
|
+
- can initialize or reuse a local MoneyDevKit agent wallet, generate local
|
|
72
|
+
receive readiness, and report only redacted wallet/payout-target refs when
|
|
73
|
+
`--setup-mdk-wallet` is explicitly set
|
|
67
74
|
- on native Windows x86_64, installs `pylon.exe` and `pylon-tui.exe` from the
|
|
68
75
|
Windows `.zip` release asset and forwards CLI subcommands to `pylon.exe`
|
|
69
76
|
- while the TUI is running on the default release track, checks GitHub Releases
|
|
@@ -87,9 +94,79 @@ The launcher:
|
|
|
87
94
|
advertise homework-worker capability. Launcher `0.1.17` adds CLI subcommand
|
|
88
95
|
forwarding and bounds background GitHub release checks to avoid
|
|
89
96
|
unauthenticated rate-limit churn.
|
|
90
|
-
- does not
|
|
91
|
-
|
|
92
|
-
|
|
97
|
+
- does not register with OpenAgents by default; registration is an explicit
|
|
98
|
+
token-scoped operator action
|
|
99
|
+
|
|
100
|
+
## OpenAgents registration
|
|
101
|
+
|
|
102
|
+
Use this only with an active OpenAgents programmatic agent token. Prefer an
|
|
103
|
+
environment variable so the token is not stored in shell history:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
export OPENAGENTS_AGENT_TOKEN="oa_agent_..."
|
|
107
|
+
|
|
108
|
+
npx @openagentsinc/pylon@latest \
|
|
109
|
+
--register-openagents \
|
|
110
|
+
--openagents-api https://openagents.com \
|
|
111
|
+
--resource-mode background_20 \
|
|
112
|
+
--no-launch \
|
|
113
|
+
--json
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
The launcher runs the normal first-run smoke, derives or reuses a stable public
|
|
117
|
+
Pylon ref from the local Pylon identity, registers through
|
|
118
|
+
`POST /api/pylons/register`, and sends a heartbeat through
|
|
119
|
+
`POST /api/pylons/{pylonRef}/heartbeat`.
|
|
120
|
+
|
|
121
|
+
Optional public-safe registration flags:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
--pylon-ref pylon.example.local
|
|
125
|
+
--pylon-display-name "Example Pylon"
|
|
126
|
+
--capability-ref capability.public.gpu
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The registration payload deliberately excludes local paths, wallet material,
|
|
130
|
+
private model inventory, hostnames, SSH details, provider credentials, and raw
|
|
131
|
+
payment data.
|
|
132
|
+
|
|
133
|
+
## MDK wallet readiness
|
|
134
|
+
|
|
135
|
+
After registration, a Pylon can report wallet receive readiness and request
|
|
136
|
+
payout-target admission:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
export OPENAGENTS_AGENT_TOKEN="oa_agent_..."
|
|
140
|
+
|
|
141
|
+
npx @openagentsinc/pylon@latest \
|
|
142
|
+
--register-openagents \
|
|
143
|
+
--setup-mdk-wallet \
|
|
144
|
+
--openagents-api https://openagents.com \
|
|
145
|
+
--resource-mode background_20 \
|
|
146
|
+
--no-launch \
|
|
147
|
+
--json
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Optional test/operator isolation flags:
|
|
151
|
+
|
|
152
|
+
```bash
|
|
153
|
+
--mdk-wallet-home /path/to/ignored/mdk-home
|
|
154
|
+
--mdk-wallet-port 3457
|
|
155
|
+
--mdk-receive-amount-sats 21
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
The launcher uses `npx @moneydevkit/agent-wallet@latest` locally. It may run
|
|
159
|
+
`init --show`, `init`, `balance`, and `receive`. It submits only:
|
|
160
|
+
|
|
161
|
+
- `wallet.public.mdk_agent_wallet.<digest>`;
|
|
162
|
+
- `receive.redacted.mdk_agent_wallet.<digest>`;
|
|
163
|
+
- `payout_target.public.mdk_agent_wallet.<digest>`;
|
|
164
|
+
- bucketed readiness refs such as
|
|
165
|
+
`balance.mdk_agent_wallet.minimum_not_satisfied`.
|
|
166
|
+
|
|
167
|
+
It does not submit the mnemonic, wallet config, raw receive invoice, payment
|
|
168
|
+
hash, preimage, exact wallet balance, wallet home path, or private destination
|
|
169
|
+
material to OpenAgents.
|
|
93
170
|
|
|
94
171
|
Set `OPENAGENTS_DISABLE_TELEMETRY=1` to disable installer telemetry, or
|
|
95
172
|
`OPENAGENTS_TELEMETRY_URL=http://127.0.0.1:8000/api/telemetry/events` to point
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -2,6 +2,7 @@ import {
|
|
|
2
2
|
DEFAULT_DIAGNOSTIC_MAX_OUTPUT_TOKENS,
|
|
3
3
|
DEFAULT_DIAGNOSTIC_REPEATS,
|
|
4
4
|
DEFAULT_MODEL_ID,
|
|
5
|
+
DEFAULT_OPENAGENTS_API_BASE,
|
|
5
6
|
DEFAULT_RELEASE_API_BASE,
|
|
6
7
|
DEFAULT_RELEASE_REPO,
|
|
7
8
|
bootstrapInstalledPylon,
|
|
@@ -120,6 +121,28 @@ Options:
|
|
|
120
121
|
--verbose Print extra network and recovery detail.
|
|
121
122
|
--debug-network Alias for --verbose.
|
|
122
123
|
--json Emit a machine-readable JSON summary.
|
|
124
|
+
--register-openagents Register and heartbeat this Pylon with
|
|
125
|
+
OpenAgents after local first-run smoke.
|
|
126
|
+
Requires OPENAGENTS_AGENT_TOKEN or
|
|
127
|
+
--openagents-agent-token.
|
|
128
|
+
--openagents-api <url> OpenAgents API base for registration.
|
|
129
|
+
Default: ${DEFAULT_OPENAGENTS_API_BASE}
|
|
130
|
+
--openagents-agent-token <token> Agent bearer token for registration.
|
|
131
|
+
Prefer the environment variable.
|
|
132
|
+
--pylon-ref <ref> Explicit public-safe Pylon ref.
|
|
133
|
+
--pylon-display-name <name> Public-safe Pylon display name.
|
|
134
|
+
--resource-mode <mode> Public resource mode for registration.
|
|
135
|
+
Default: background_20
|
|
136
|
+
--capability-ref <ref> Add a public-safe capability ref.
|
|
137
|
+
May be repeated or comma-separated.
|
|
138
|
+
--setup-mdk-wallet Initialize or reuse the local MDK agent
|
|
139
|
+
wallet, create receive readiness, and
|
|
140
|
+
report redacted wallet/payout refs.
|
|
141
|
+
--mdk-wallet-home <path> HOME override for isolated MDK wallet
|
|
142
|
+
config during tests or operator smokes.
|
|
143
|
+
--mdk-wallet-port <port> MDK agent-wallet daemon port override.
|
|
144
|
+
--mdk-receive-amount-sats <amount> Tiny receive amount for readiness.
|
|
145
|
+
Default: 1 satoshi of bitcoin.
|
|
123
146
|
|
|
124
147
|
Test and maintainer options:
|
|
125
148
|
--repo <owner/name> Override the GitHub release repo.
|
|
@@ -147,6 +170,17 @@ export function parseArgs(argv) {
|
|
|
147
170
|
json: false,
|
|
148
171
|
help: false,
|
|
149
172
|
pylonArgs: [],
|
|
173
|
+
openAgentsRegister: false,
|
|
174
|
+
openAgentsApiBase: DEFAULT_OPENAGENTS_API_BASE,
|
|
175
|
+
openAgentsAgentToken: null,
|
|
176
|
+
openAgentsPylonRef: null,
|
|
177
|
+
openAgentsPylonDisplayName: null,
|
|
178
|
+
openAgentsResourceMode: "background_20",
|
|
179
|
+
openAgentsCapabilityRefs: [],
|
|
180
|
+
openAgentsSetupMdkWallet: false,
|
|
181
|
+
openAgentsMdkWalletHome: null,
|
|
182
|
+
openAgentsMdkWalletPort: null,
|
|
183
|
+
openAgentsMdkReceiveAmountSats: 1,
|
|
150
184
|
};
|
|
151
185
|
|
|
152
186
|
for (let index = 0; index < argv.length; index += 1) {
|
|
@@ -227,6 +261,73 @@ export function parseArgs(argv) {
|
|
|
227
261
|
case "--json":
|
|
228
262
|
options.json = true;
|
|
229
263
|
break;
|
|
264
|
+
case "--register-openagents":
|
|
265
|
+
options.openAgentsRegister = true;
|
|
266
|
+
break;
|
|
267
|
+
case "--openagents-api":
|
|
268
|
+
options.openAgentsApiBase = argv[++index];
|
|
269
|
+
if (!options.openAgentsApiBase) {
|
|
270
|
+
throw new Error("--openagents-api requires a value.");
|
|
271
|
+
}
|
|
272
|
+
break;
|
|
273
|
+
case "--openagents-agent-token":
|
|
274
|
+
options.openAgentsAgentToken = argv[++index];
|
|
275
|
+
if (!options.openAgentsAgentToken) {
|
|
276
|
+
throw new Error("--openagents-agent-token requires a value.");
|
|
277
|
+
}
|
|
278
|
+
break;
|
|
279
|
+
case "--pylon-ref":
|
|
280
|
+
options.openAgentsPylonRef = argv[++index];
|
|
281
|
+
if (!options.openAgentsPylonRef) {
|
|
282
|
+
throw new Error("--pylon-ref requires a value.");
|
|
283
|
+
}
|
|
284
|
+
break;
|
|
285
|
+
case "--pylon-display-name":
|
|
286
|
+
options.openAgentsPylonDisplayName = argv[++index];
|
|
287
|
+
if (!options.openAgentsPylonDisplayName) {
|
|
288
|
+
throw new Error("--pylon-display-name requires a value.");
|
|
289
|
+
}
|
|
290
|
+
break;
|
|
291
|
+
case "--resource-mode":
|
|
292
|
+
options.openAgentsResourceMode = argv[++index];
|
|
293
|
+
if (!options.openAgentsResourceMode) {
|
|
294
|
+
throw new Error("--resource-mode requires a value.");
|
|
295
|
+
}
|
|
296
|
+
break;
|
|
297
|
+
case "--capability-ref": {
|
|
298
|
+
const value = argv[++index];
|
|
299
|
+
if (!value) {
|
|
300
|
+
throw new Error("--capability-ref requires a value.");
|
|
301
|
+
}
|
|
302
|
+
options.openAgentsCapabilityRefs.push(
|
|
303
|
+
...value
|
|
304
|
+
.split(",")
|
|
305
|
+
.map((entry) => entry.trim())
|
|
306
|
+
.filter(Boolean),
|
|
307
|
+
);
|
|
308
|
+
break;
|
|
309
|
+
}
|
|
310
|
+
case "--setup-mdk-wallet":
|
|
311
|
+
options.openAgentsSetupMdkWallet = true;
|
|
312
|
+
break;
|
|
313
|
+
case "--mdk-wallet-home":
|
|
314
|
+
options.openAgentsMdkWalletHome = argv[++index];
|
|
315
|
+
if (!options.openAgentsMdkWalletHome) {
|
|
316
|
+
throw new Error("--mdk-wallet-home requires a value.");
|
|
317
|
+
}
|
|
318
|
+
break;
|
|
319
|
+
case "--mdk-wallet-port":
|
|
320
|
+
options.openAgentsMdkWalletPort = parseIntegerFlag(
|
|
321
|
+
argv[++index],
|
|
322
|
+
"--mdk-wallet-port",
|
|
323
|
+
);
|
|
324
|
+
break;
|
|
325
|
+
case "--mdk-receive-amount-sats":
|
|
326
|
+
options.openAgentsMdkReceiveAmountSats = parseIntegerFlag(
|
|
327
|
+
argv[++index],
|
|
328
|
+
"--mdk-receive-amount-sats",
|
|
329
|
+
);
|
|
330
|
+
break;
|
|
230
331
|
case "--repo":
|
|
231
332
|
options.repo = argv[++index];
|
|
232
333
|
if (!options.repo) {
|
package/src/index.js
CHANGED
|
@@ -15,6 +15,7 @@ export { createTelemetryClient } from "./telemetry.js";
|
|
|
15
15
|
export const DEFAULT_RELEASE_REPO = "OpenAgentsInc/openagents";
|
|
16
16
|
export const DEFAULT_RELEASE_API_BASE = "https://api.github.com";
|
|
17
17
|
export const DEFAULT_RELEASE_GIT_BASE = "https://github.com";
|
|
18
|
+
export const DEFAULT_OPENAGENTS_API_BASE = "https://openagents.com";
|
|
18
19
|
export const DEFAULT_RUSTUP_INIT_URL = "https://sh.rustup.rs";
|
|
19
20
|
export const DEFAULT_MODEL_ID = "gemma-4-e4b";
|
|
20
21
|
export const DEFAULT_DIAGNOSTIC_REPEATS = 3;
|
|
@@ -26,6 +27,12 @@ const PYLON_RELEASE_TAG_PREFIX = "pylon-v";
|
|
|
26
27
|
const RELEASE_ASSET_INSTALL_METHOD = "release_asset";
|
|
27
28
|
const SOURCE_BUILD_INSTALL_METHOD = "source_build";
|
|
28
29
|
const PREFERRED_RUNTIME_MODEL_NAME = "gemma4:e4b";
|
|
30
|
+
const DEFAULT_PYLON_RESOURCE_MODE = "background_20";
|
|
31
|
+
const DEFAULT_MDK_RECEIVE_AMOUNT_SATS = 1;
|
|
32
|
+
const DEFAULT_PYLON_CAPABILITY_REFS = [
|
|
33
|
+
"capability.public.pylon_launcher",
|
|
34
|
+
"capability.public.inference",
|
|
35
|
+
];
|
|
29
36
|
const LEGACY_SOURCE_BUILD_SIBLING_REPOSITORIES = {
|
|
30
37
|
"spark-sdk": "https://github.com/AtlantisPleb/spark-sdk.git",
|
|
31
38
|
};
|
|
@@ -1549,6 +1556,407 @@ function isUnsupportedGemmaDiagnoseError(error) {
|
|
|
1549
1556
|
);
|
|
1550
1557
|
}
|
|
1551
1558
|
|
|
1559
|
+
function publicHash(value) {
|
|
1560
|
+
return createHash("sha256").update(String(value)).digest("hex").slice(0, 16);
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
function firstString(...values) {
|
|
1564
|
+
for (const value of values) {
|
|
1565
|
+
if (typeof value === "string" && value.trim() !== "") {
|
|
1566
|
+
return value.trim();
|
|
1567
|
+
}
|
|
1568
|
+
}
|
|
1569
|
+
return null;
|
|
1570
|
+
}
|
|
1571
|
+
|
|
1572
|
+
function normalizePublicPylonRef(value) {
|
|
1573
|
+
const normalized = String(value ?? "")
|
|
1574
|
+
.trim()
|
|
1575
|
+
.toLowerCase()
|
|
1576
|
+
.replace(/[^a-z0-9_.:-]+/g, "-")
|
|
1577
|
+
.replace(/^[^a-z0-9]+/, "")
|
|
1578
|
+
.slice(0, 120);
|
|
1579
|
+
|
|
1580
|
+
if (!normalized || normalized.length < 3) {
|
|
1581
|
+
return null;
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
return normalized;
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
export function resolveOpenAgentsPylonRef({
|
|
1588
|
+
explicitPylonRef = null,
|
|
1589
|
+
init = null,
|
|
1590
|
+
status = null,
|
|
1591
|
+
configPath = null,
|
|
1592
|
+
target = null,
|
|
1593
|
+
} = {}) {
|
|
1594
|
+
const explicit = normalizePublicPylonRef(explicitPylonRef);
|
|
1595
|
+
if (explicit) {
|
|
1596
|
+
return explicit;
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
const source = firstString(
|
|
1600
|
+
init?.pylon_ref,
|
|
1601
|
+
init?.pylonRef,
|
|
1602
|
+
init?.identity_ref,
|
|
1603
|
+
init?.identityRef,
|
|
1604
|
+
init?.node_id,
|
|
1605
|
+
init?.nodeId,
|
|
1606
|
+
init?.public_key,
|
|
1607
|
+
init?.publicKey,
|
|
1608
|
+
init?.identity?.pylon_ref,
|
|
1609
|
+
init?.identity?.node_id,
|
|
1610
|
+
init?.identity?.public_key,
|
|
1611
|
+
status?.pylon_ref,
|
|
1612
|
+
status?.pylonRef,
|
|
1613
|
+
status?.node_id,
|
|
1614
|
+
status?.nodeId,
|
|
1615
|
+
status?.snapshot?.identity?.pylon_ref,
|
|
1616
|
+
status?.snapshot?.identity?.node_id,
|
|
1617
|
+
status?.snapshot?.identity?.public_key,
|
|
1618
|
+
configPath,
|
|
1619
|
+
);
|
|
1620
|
+
|
|
1621
|
+
if (!source) {
|
|
1622
|
+
return null;
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
const platform = target?.os && target?.arch ? `${target.os}.${target.arch}` : "local";
|
|
1626
|
+
return `pylon.${platform}.${publicHash(source)}`.slice(0, 120);
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
function statusLabelFromPylonStatus(status) {
|
|
1630
|
+
const authoritative = firstString(
|
|
1631
|
+
status?.snapshot?.runtime?.authoritative_status,
|
|
1632
|
+
status?.snapshot?.runtime?.status,
|
|
1633
|
+
status?.runtime?.authoritative_status,
|
|
1634
|
+
status?.status,
|
|
1635
|
+
);
|
|
1636
|
+
|
|
1637
|
+
if (!authoritative) {
|
|
1638
|
+
return "not_ready";
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
const normalized = authoritative.toLowerCase();
|
|
1642
|
+
if (normalized.includes("ready") || normalized.includes("online")) {
|
|
1643
|
+
return "online";
|
|
1644
|
+
}
|
|
1645
|
+
if (normalized.includes("degraded")) {
|
|
1646
|
+
return "degraded";
|
|
1647
|
+
}
|
|
1648
|
+
if (normalized.includes("blocked") || normalized.includes("error")) {
|
|
1649
|
+
return "blocked";
|
|
1650
|
+
}
|
|
1651
|
+
return normalized.slice(0, 80);
|
|
1652
|
+
}
|
|
1653
|
+
|
|
1654
|
+
function capabilityRefsFromOptions(options, inventory) {
|
|
1655
|
+
const explicit = Array.isArray(options.openAgentsCapabilityRefs)
|
|
1656
|
+
? options.openAgentsCapabilityRefs
|
|
1657
|
+
: [];
|
|
1658
|
+
const refs = new Set(
|
|
1659
|
+
[...DEFAULT_PYLON_CAPABILITY_REFS, ...explicit]
|
|
1660
|
+
.map((ref) => String(ref).trim())
|
|
1661
|
+
.filter(Boolean),
|
|
1662
|
+
);
|
|
1663
|
+
|
|
1664
|
+
if (Array.isArray(inventory?.rows) && inventory.rows.length > 0) {
|
|
1665
|
+
refs.add("capability.public.local_inventory_present");
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
return Array.from(refs);
|
|
1669
|
+
}
|
|
1670
|
+
|
|
1671
|
+
async function postOpenAgentsJson({
|
|
1672
|
+
fetchImpl,
|
|
1673
|
+
url,
|
|
1674
|
+
token,
|
|
1675
|
+
idempotencyKey,
|
|
1676
|
+
body,
|
|
1677
|
+
}) {
|
|
1678
|
+
const response = await fetchImpl(url, {
|
|
1679
|
+
method: "POST",
|
|
1680
|
+
headers: {
|
|
1681
|
+
Authorization: `Bearer ${token}`,
|
|
1682
|
+
"Content-Type": "application/json",
|
|
1683
|
+
"Idempotency-Key": idempotencyKey,
|
|
1684
|
+
},
|
|
1685
|
+
body: JSON.stringify(body),
|
|
1686
|
+
});
|
|
1687
|
+
const text = await response.text();
|
|
1688
|
+
const payload = text.trim() ? JSON.parse(text) : null;
|
|
1689
|
+
|
|
1690
|
+
if (!response.ok) {
|
|
1691
|
+
const reason =
|
|
1692
|
+
payload?.reason ??
|
|
1693
|
+
payload?.error ??
|
|
1694
|
+
`${response.status} ${response.statusText}`.trim();
|
|
1695
|
+
throw new Error(`OpenAgents Pylon registration failed: ${reason}`);
|
|
1696
|
+
}
|
|
1697
|
+
|
|
1698
|
+
return payload;
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
export async function registerPylonWithOpenAgents(
|
|
1702
|
+
options,
|
|
1703
|
+
{
|
|
1704
|
+
init,
|
|
1705
|
+
status,
|
|
1706
|
+
inventory,
|
|
1707
|
+
fetchImpl = globalThis.fetch,
|
|
1708
|
+
onStatus = null,
|
|
1709
|
+
} = {},
|
|
1710
|
+
) {
|
|
1711
|
+
if (!options.openAgentsRegister) {
|
|
1712
|
+
return null;
|
|
1713
|
+
}
|
|
1714
|
+
|
|
1715
|
+
const token =
|
|
1716
|
+
options.openAgentsAgentToken ??
|
|
1717
|
+
process.env.OPENAGENTS_AGENT_TOKEN ??
|
|
1718
|
+
process.env.OPENAGENTS_PYLON_AGENT_TOKEN;
|
|
1719
|
+
|
|
1720
|
+
if (!token) {
|
|
1721
|
+
throw new Error(
|
|
1722
|
+
"OpenAgents registration requires OPENAGENTS_AGENT_TOKEN or --openagents-agent-token.",
|
|
1723
|
+
);
|
|
1724
|
+
}
|
|
1725
|
+
|
|
1726
|
+
if (typeof fetchImpl !== "function") {
|
|
1727
|
+
throw new Error("A fetch implementation is required for OpenAgents registration.");
|
|
1728
|
+
}
|
|
1729
|
+
|
|
1730
|
+
const apiBase = (options.openAgentsApiBase ?? DEFAULT_OPENAGENTS_API_BASE)
|
|
1731
|
+
.replace(/\/+$/, "");
|
|
1732
|
+
const pylonRef = resolveOpenAgentsPylonRef({
|
|
1733
|
+
explicitPylonRef: options.openAgentsPylonRef,
|
|
1734
|
+
init,
|
|
1735
|
+
status,
|
|
1736
|
+
configPath: init?.config_path ?? options.configPath ?? null,
|
|
1737
|
+
target: options.target,
|
|
1738
|
+
});
|
|
1739
|
+
|
|
1740
|
+
if (!pylonRef) {
|
|
1741
|
+
throw new Error(
|
|
1742
|
+
"OpenAgents registration could not derive a stable public Pylon ref; pass --pylon-ref.",
|
|
1743
|
+
);
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
const resourceMode =
|
|
1747
|
+
options.openAgentsResourceMode ?? DEFAULT_PYLON_RESOURCE_MODE;
|
|
1748
|
+
const displayName =
|
|
1749
|
+
options.openAgentsPylonDisplayName ?? `Pylon ${pylonRef.slice(-8)}`;
|
|
1750
|
+
const heartbeatStatus = statusLabelFromPylonStatus(status);
|
|
1751
|
+
|
|
1752
|
+
emitStatus(onStatus, "Registering Pylon with OpenAgents", pylonRef);
|
|
1753
|
+
const registration = await postOpenAgentsJson({
|
|
1754
|
+
fetchImpl,
|
|
1755
|
+
url: `${apiBase}/api/pylons/register`,
|
|
1756
|
+
token,
|
|
1757
|
+
idempotencyKey: `pylon-register-${pylonRef}`,
|
|
1758
|
+
body: {
|
|
1759
|
+
capabilityRefs: capabilityRefsFromOptions(options, inventory),
|
|
1760
|
+
displayName,
|
|
1761
|
+
pylonRef,
|
|
1762
|
+
resourceMode,
|
|
1763
|
+
statusRefs: [
|
|
1764
|
+
`status.public.${heartbeatStatus}`,
|
|
1765
|
+
`release.public.${String(options.tagName ?? `pylon-v${options.version}`).replace(/[^A-Za-z0-9_.:/-]+/g, "_")}`,
|
|
1766
|
+
],
|
|
1767
|
+
},
|
|
1768
|
+
});
|
|
1769
|
+
|
|
1770
|
+
emitStatus(onStatus, "Sending OpenAgents Pylon heartbeat", heartbeatStatus);
|
|
1771
|
+
const heartbeat = await postOpenAgentsJson({
|
|
1772
|
+
fetchImpl,
|
|
1773
|
+
url: `${apiBase}/api/pylons/${encodeURIComponent(pylonRef)}/heartbeat`,
|
|
1774
|
+
token,
|
|
1775
|
+
idempotencyKey: `pylon-heartbeat-${pylonRef}-${Math.floor(Date.now() / 60_000)}`,
|
|
1776
|
+
body: {
|
|
1777
|
+
capacityRefs: ["capacity.public.launcher_smoke"],
|
|
1778
|
+
healthRefs: [`health.public.${heartbeatStatus}`],
|
|
1779
|
+
resourceMode,
|
|
1780
|
+
status: heartbeatStatus,
|
|
1781
|
+
},
|
|
1782
|
+
});
|
|
1783
|
+
|
|
1784
|
+
return {
|
|
1785
|
+
apiBase,
|
|
1786
|
+
pylonRef,
|
|
1787
|
+
resourceMode,
|
|
1788
|
+
status: heartbeatStatus,
|
|
1789
|
+
registration: {
|
|
1790
|
+
idempotent: Boolean(registration?.idempotent),
|
|
1791
|
+
publicUrl: `${apiBase}/api/pylons/${encodeURIComponent(pylonRef)}`,
|
|
1792
|
+
},
|
|
1793
|
+
heartbeat: {
|
|
1794
|
+
idempotent: Boolean(heartbeat?.idempotent),
|
|
1795
|
+
},
|
|
1796
|
+
};
|
|
1797
|
+
}
|
|
1798
|
+
|
|
1799
|
+
function buildMdkAgentWalletEnv(options = {}) {
|
|
1800
|
+
const env = { ...process.env };
|
|
1801
|
+
if (options.openAgentsMdkWalletHome) {
|
|
1802
|
+
env.HOME = path.resolve(options.openAgentsMdkWalletHome);
|
|
1803
|
+
}
|
|
1804
|
+
if (options.openAgentsMdkWalletPort) {
|
|
1805
|
+
env.MDK_WALLET_PORT = String(options.openAgentsMdkWalletPort);
|
|
1806
|
+
}
|
|
1807
|
+
return env;
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
async function runMdkAgentWalletJson(args, options, runProcessImpl) {
|
|
1811
|
+
const { stdout } = await runProcessImpl(
|
|
1812
|
+
"npx",
|
|
1813
|
+
["-y", "@moneydevkit/agent-wallet@latest", ...args],
|
|
1814
|
+
{ env: buildMdkAgentWalletEnv(options) },
|
|
1815
|
+
);
|
|
1816
|
+
try {
|
|
1817
|
+
return JSON.parse(stdout);
|
|
1818
|
+
} catch (error) {
|
|
1819
|
+
throw new Error(
|
|
1820
|
+
`MDK agent wallet returned invalid JSON for \`agent-wallet ${args.join(" ")}\`: ${
|
|
1821
|
+
error instanceof Error ? error.message : String(error)
|
|
1822
|
+
}`,
|
|
1823
|
+
);
|
|
1824
|
+
}
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1827
|
+
export async function setupMdkWalletReadinessForOpenAgentsPylon(
|
|
1828
|
+
options,
|
|
1829
|
+
{
|
|
1830
|
+
fetchImpl = globalThis.fetch,
|
|
1831
|
+
init = null,
|
|
1832
|
+
status = null,
|
|
1833
|
+
registration = null,
|
|
1834
|
+
runProcessImpl = runProcess,
|
|
1835
|
+
onStatus = null,
|
|
1836
|
+
} = {},
|
|
1837
|
+
) {
|
|
1838
|
+
if (!options.openAgentsSetupMdkWallet) {
|
|
1839
|
+
return null;
|
|
1840
|
+
}
|
|
1841
|
+
|
|
1842
|
+
const token =
|
|
1843
|
+
options.openAgentsAgentToken ??
|
|
1844
|
+
process.env.OPENAGENTS_AGENT_TOKEN ??
|
|
1845
|
+
process.env.OPENAGENTS_PYLON_AGENT_TOKEN;
|
|
1846
|
+
|
|
1847
|
+
if (!token) {
|
|
1848
|
+
throw new Error(
|
|
1849
|
+
"MDK wallet readiness reporting requires OPENAGENTS_AGENT_TOKEN or --openagents-agent-token.",
|
|
1850
|
+
);
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
const pylonRef =
|
|
1854
|
+
registration?.pylonRef ??
|
|
1855
|
+
resolveOpenAgentsPylonRef({
|
|
1856
|
+
explicitPylonRef: options.openAgentsPylonRef,
|
|
1857
|
+
init,
|
|
1858
|
+
status,
|
|
1859
|
+
configPath: init?.config_path ?? options.configPath ?? null,
|
|
1860
|
+
target: options.target,
|
|
1861
|
+
});
|
|
1862
|
+
|
|
1863
|
+
if (!pylonRef) {
|
|
1864
|
+
throw new Error(
|
|
1865
|
+
"MDK wallet readiness could not derive a stable public Pylon ref; pass --pylon-ref.",
|
|
1866
|
+
);
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
const apiBase = (options.openAgentsApiBase ?? DEFAULT_OPENAGENTS_API_BASE)
|
|
1870
|
+
.replace(/\/+$/, "");
|
|
1871
|
+
const receiveAmount = Number.isFinite(options.openAgentsMdkReceiveAmountSats)
|
|
1872
|
+
? options.openAgentsMdkReceiveAmountSats
|
|
1873
|
+
: DEFAULT_MDK_RECEIVE_AMOUNT_SATS;
|
|
1874
|
+
|
|
1875
|
+
emitStatus(onStatus, "Preparing MDK agent wallet readiness", pylonRef);
|
|
1876
|
+
let walletInit;
|
|
1877
|
+
try {
|
|
1878
|
+
walletInit = await runMdkAgentWalletJson(["init", "--show"], options, runProcessImpl);
|
|
1879
|
+
} catch {
|
|
1880
|
+
walletInit = await runMdkAgentWalletJson(["init"], options, runProcessImpl);
|
|
1881
|
+
}
|
|
1882
|
+
|
|
1883
|
+
const balance = await runMdkAgentWalletJson(["balance"], options, runProcessImpl);
|
|
1884
|
+
const receive = await runMdkAgentWalletJson(
|
|
1885
|
+
[
|
|
1886
|
+
"receive",
|
|
1887
|
+
String(receiveAmount),
|
|
1888
|
+
"--description",
|
|
1889
|
+
`OpenAgents Pylon readiness ${pylonRef}`,
|
|
1890
|
+
],
|
|
1891
|
+
options,
|
|
1892
|
+
runProcessImpl,
|
|
1893
|
+
);
|
|
1894
|
+
|
|
1895
|
+
const walletRef = `wallet.public.mdk_agent_wallet.${publicHash(
|
|
1896
|
+
firstString(walletInit?.walletId, walletInit?.wallet_id, pylonRef) ?? pylonRef,
|
|
1897
|
+
)}`;
|
|
1898
|
+
const receiveRef = `receive.redacted.mdk_agent_wallet.${publicHash(
|
|
1899
|
+
firstString(receive?.invoice, receive?.payment_hash, receive?.paymentHash, pylonRef) ?? pylonRef,
|
|
1900
|
+
)}`;
|
|
1901
|
+
const payoutTargetRef = `payout_target.public.mdk_agent_wallet.${publicHash(
|
|
1902
|
+
`${pylonRef}:${receiveRef}`,
|
|
1903
|
+
)}`;
|
|
1904
|
+
const balanceReady =
|
|
1905
|
+
typeof balance?.balance_sats === "number" && balance.balance_sats > 0
|
|
1906
|
+
? "balance.mdk_agent_wallet.minimum_satisfied"
|
|
1907
|
+
: "balance.mdk_agent_wallet.minimum_not_satisfied";
|
|
1908
|
+
|
|
1909
|
+
emitStatus(onStatus, "Reporting MDK wallet readiness", pylonRef);
|
|
1910
|
+
const walletReadiness = await postOpenAgentsJson({
|
|
1911
|
+
fetchImpl,
|
|
1912
|
+
url: `${apiBase}/api/pylons/${encodeURIComponent(pylonRef)}/wallet-readiness`,
|
|
1913
|
+
token,
|
|
1914
|
+
idempotencyKey: `pylon-wallet-readiness-${pylonRef}-${receiveRef}`,
|
|
1915
|
+
body: {
|
|
1916
|
+
balanceRefs: [balanceReady],
|
|
1917
|
+
liquidityRefs: ["liquidity.public.receive_ready"],
|
|
1918
|
+
readinessRefs: [
|
|
1919
|
+
"readiness.public.mdk_agent_wallet_initialized",
|
|
1920
|
+
"readiness.public.mdk_agent_wallet_receive_ready",
|
|
1921
|
+
receiveRef,
|
|
1922
|
+
],
|
|
1923
|
+
status: "ready",
|
|
1924
|
+
walletReady: true,
|
|
1925
|
+
walletRef,
|
|
1926
|
+
},
|
|
1927
|
+
});
|
|
1928
|
+
|
|
1929
|
+
emitStatus(onStatus, "Requesting OpenAgents payout-target admission", pylonRef);
|
|
1930
|
+
const payoutTargetAdmission = await postOpenAgentsJson({
|
|
1931
|
+
fetchImpl,
|
|
1932
|
+
url: `${apiBase}/api/pylons/${encodeURIComponent(pylonRef)}/payout-target-admission`,
|
|
1933
|
+
token,
|
|
1934
|
+
idempotencyKey: `pylon-payout-target-${pylonRef}-${payoutTargetRef}`,
|
|
1935
|
+
body: {
|
|
1936
|
+
admissionRefs: ["admission.public.requested", receiveRef],
|
|
1937
|
+
payoutTargetRef,
|
|
1938
|
+
policyRefs: ["policy.public.operator_review_required"],
|
|
1939
|
+
status: "requested",
|
|
1940
|
+
},
|
|
1941
|
+
});
|
|
1942
|
+
|
|
1943
|
+
return {
|
|
1944
|
+
pylonRef,
|
|
1945
|
+
walletReady: true,
|
|
1946
|
+
walletRef,
|
|
1947
|
+
receiveRef,
|
|
1948
|
+
payoutTargetRef,
|
|
1949
|
+
balanceReadinessRef: balanceReady,
|
|
1950
|
+
walletReadiness: {
|
|
1951
|
+
idempotent: Boolean(walletReadiness?.idempotent),
|
|
1952
|
+
},
|
|
1953
|
+
payoutTargetAdmission: {
|
|
1954
|
+
idempotent: Boolean(payoutTargetAdmission?.idempotent),
|
|
1955
|
+
status: "requested",
|
|
1956
|
+
},
|
|
1957
|
+
};
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1552
1960
|
export async function ensureReleaseInstall(
|
|
1553
1961
|
options = {},
|
|
1554
1962
|
{
|
|
@@ -1879,6 +2287,7 @@ export async function ensureReleaseInstall(
|
|
|
1879
2287
|
export async function bootstrapInstalledPylon(
|
|
1880
2288
|
options,
|
|
1881
2289
|
{
|
|
2290
|
+
fetchImpl = globalThis.fetch,
|
|
1882
2291
|
runProcessImpl = runProcess,
|
|
1883
2292
|
onStatus = null,
|
|
1884
2293
|
telemetryClient = null,
|
|
@@ -1928,6 +2337,25 @@ export async function bootstrapInstalledPylon(
|
|
|
1928
2337
|
runProcessImpl,
|
|
1929
2338
|
);
|
|
1930
2339
|
|
|
2340
|
+
const openAgentsRegistration = await registerPylonWithOpenAgents(options, {
|
|
2341
|
+
fetchImpl,
|
|
2342
|
+
init,
|
|
2343
|
+
inventory,
|
|
2344
|
+
onStatus,
|
|
2345
|
+
status,
|
|
2346
|
+
});
|
|
2347
|
+
const openAgentsMdkWallet = await setupMdkWalletReadinessForOpenAgentsPylon(
|
|
2348
|
+
options,
|
|
2349
|
+
{
|
|
2350
|
+
fetchImpl,
|
|
2351
|
+
init,
|
|
2352
|
+
onStatus,
|
|
2353
|
+
registration: openAgentsRegistration,
|
|
2354
|
+
runProcessImpl,
|
|
2355
|
+
status,
|
|
2356
|
+
},
|
|
2357
|
+
);
|
|
2358
|
+
|
|
1931
2359
|
let download = null;
|
|
1932
2360
|
if (!skipModelDownload) {
|
|
1933
2361
|
emitStatus(onStatus, "Downloading curated model bundle", model);
|
|
@@ -2025,6 +2453,8 @@ export async function bootstrapInstalledPylon(
|
|
|
2025
2453
|
init,
|
|
2026
2454
|
status,
|
|
2027
2455
|
inventory,
|
|
2456
|
+
openAgentsRegistration,
|
|
2457
|
+
openAgentsMdkWallet,
|
|
2028
2458
|
model,
|
|
2029
2459
|
download,
|
|
2030
2460
|
diagnostic,
|