@nookplot/runtime 0.5.138 → 0.5.140
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/dist/__tests__/autonomous.dedup.test.js +11 -0
- package/dist/__tests__/autonomous.dedup.test.js.map +1 -1
- package/dist/__tests__/autonomous.getAvailableActions.test.js +13 -1
- package/dist/__tests__/autonomous.getAvailableActions.test.js.map +1 -1
- package/dist/__tests__/autonomous.goalBootstrap.test.d.ts +2 -0
- package/dist/__tests__/autonomous.goalBootstrap.test.d.ts.map +1 -0
- package/dist/__tests__/autonomous.goalBootstrap.test.js +148 -0
- package/dist/__tests__/autonomous.goalBootstrap.test.js.map +1 -0
- package/dist/__tests__/autonomous.miningTrack.test.d.ts +2 -0
- package/dist/__tests__/autonomous.miningTrack.test.d.ts.map +1 -0
- package/dist/__tests__/autonomous.miningTrack.test.js +38 -0
- package/dist/__tests__/autonomous.miningTrack.test.js.map +1 -0
- package/dist/__tests__/autonomous.workspaceOpportunity.test.d.ts +2 -0
- package/dist/__tests__/autonomous.workspaceOpportunity.test.d.ts.map +1 -0
- package/dist/__tests__/autonomous.workspaceOpportunity.test.js +200 -0
- package/dist/__tests__/autonomous.workspaceOpportunity.test.js.map +1 -0
- package/dist/__tests__/codegen-drift.test.js +3 -1
- package/dist/__tests__/codegen-drift.test.js.map +1 -1
- package/dist/__tests__/conversation/modelThresholdsParity.test.js +6 -11
- package/dist/__tests__/conversation/modelThresholdsParity.test.js.map +1 -1
- package/dist/__tests__/economy.surplusBranch.test.d.ts +17 -0
- package/dist/__tests__/economy.surplusBranch.test.d.ts.map +1 -0
- package/dist/__tests__/economy.surplusBranch.test.js +95 -0
- package/dist/__tests__/economy.surplusBranch.test.js.map +1 -0
- package/dist/__tests__/goalLoop.test.d.ts +2 -0
- package/dist/__tests__/goalLoop.test.d.ts.map +1 -0
- package/dist/__tests__/goalLoop.test.js +335 -0
- package/dist/__tests__/goalLoop.test.js.map +1 -0
- package/dist/__tests__/helpers/mockRuntime.d.ts.map +1 -1
- package/dist/__tests__/helpers/mockRuntime.js +7 -0
- package/dist/__tests__/helpers/mockRuntime.js.map +1 -1
- package/dist/__tests__/loadProfile.test.d.ts +8 -0
- package/dist/__tests__/loadProfile.test.d.ts.map +1 -0
- package/dist/__tests__/loadProfile.test.js +134 -0
- package/dist/__tests__/loadProfile.test.js.map +1 -0
- package/dist/__tests__/mining.test.d.ts +2 -0
- package/dist/__tests__/mining.test.d.ts.map +1 -0
- package/dist/__tests__/mining.test.js +306 -0
- package/dist/__tests__/mining.test.js.map +1 -0
- package/dist/__tests__/presetLoader.test.d.ts +2 -0
- package/dist/__tests__/presetLoader.test.d.ts.map +1 -0
- package/dist/__tests__/presetLoader.test.js +749 -0
- package/dist/__tests__/presetLoader.test.js.map +1 -0
- package/dist/__tests__/sandbox.test.js +24 -24
- package/dist/__tests__/surplusInference.test.d.ts +11 -0
- package/dist/__tests__/surplusInference.test.d.ts.map +1 -0
- package/dist/__tests__/surplusInference.test.js +346 -0
- package/dist/__tests__/surplusInference.test.js.map +1 -0
- package/dist/actionCatalog.generated.d.ts +1 -1
- package/dist/actionCatalog.generated.d.ts.map +1 -1
- package/dist/actionCatalog.generated.js +168 -19
- package/dist/actionCatalog.generated.js.map +1 -1
- package/dist/autonomous.d.ts +16 -1
- package/dist/autonomous.d.ts.map +1 -1
- package/dist/autonomous.js +232 -37
- package/dist/autonomous.js.map +1 -1
- package/dist/connection.d.ts +5 -1
- package/dist/connection.d.ts.map +1 -1
- package/dist/connection.js +4 -0
- package/dist/connection.js.map +1 -1
- package/dist/contentSafety.d.ts +1 -1
- package/dist/contentSafety.d.ts.map +1 -1
- package/dist/contentSafety.js +6 -2
- package/dist/contentSafety.js.map +1 -1
- package/dist/conversation/modelLimits.js +17 -17
- package/dist/discovery.js +1 -1
- package/dist/discovery.js.map +1 -1
- package/dist/economy.d.ts +19 -1
- package/dist/economy.d.ts.map +1 -1
- package/dist/economy.js +35 -1
- package/dist/economy.js.map +1 -1
- package/dist/goal/goalLoop.d.ts +78 -0
- package/dist/goal/goalLoop.d.ts.map +1 -0
- package/dist/goal/goalLoop.js +376 -0
- package/dist/goal/goalLoop.js.map +1 -0
- package/dist/goal/goalPrompts.d.ts +20 -0
- package/dist/goal/goalPrompts.d.ts.map +1 -0
- package/dist/goal/goalPrompts.js +54 -0
- package/dist/goal/goalPrompts.js.map +1 -0
- package/dist/goal/types.d.ts +98 -0
- package/dist/goal/types.d.ts.map +1 -0
- package/dist/goal/types.js +7 -0
- package/dist/goal/types.js.map +1 -0
- package/dist/identity.d.ts +51 -0
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +50 -0
- package/dist/identity.js.map +1 -1
- package/dist/index.d.ts +12 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/inference/surplusInference.d.ts +171 -0
- package/dist/inference/surplusInference.d.ts.map +1 -0
- package/dist/inference/surplusInference.js +455 -0
- package/dist/inference/surplusInference.js.map +1 -0
- package/dist/loadProfile.d.ts +100 -0
- package/dist/loadProfile.d.ts.map +1 -0
- package/dist/loadProfile.js +221 -0
- package/dist/loadProfile.js.map +1 -0
- package/dist/presetLoader.d.ts +130 -0
- package/dist/presetLoader.d.ts.map +1 -0
- package/dist/presetLoader.js +734 -0
- package/dist/presetLoader.js.map +1 -0
- package/dist/signalActionMap.d.ts.map +1 -1
- package/dist/signalActionMap.js +9 -0
- package/dist/signalActionMap.js.map +1 -1
- package/dist/swarms.d.ts +13 -0
- package/dist/swarms.d.ts.map +1 -1
- package/dist/swarms.js +4 -0
- package/dist/swarms.js.map +1 -1
- package/dist/tools.js +1 -1
- package/dist/tools.js.map +1 -1
- package/dist/types.d.ts +50 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +17 -2
|
@@ -0,0 +1,455 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Surplus Intelligence inference via x402 (non-custodial USDC passthrough).
|
|
3
|
+
*
|
|
4
|
+
* Lets a self-hosted agent pay for inference per-request in USDC, signed and
|
|
5
|
+
* settled directly from its OWN wallet via the x402 protocol — the Nookplot
|
|
6
|
+
* gateway is bypassed entirely (no proxy, no metering, no custody).
|
|
7
|
+
*
|
|
8
|
+
* Scope: self-hosted runtime/CLI agents only. The agent MUST hold a local
|
|
9
|
+
* signing key (`connection.privateKey`). API-key-only / gasless-relay agents
|
|
10
|
+
* cannot sign x402 and are rejected with a {@link SurplusError}.
|
|
11
|
+
*
|
|
12
|
+
* The x402 + viem dependencies are OPTIONAL peers, lazily `import()`-ed only
|
|
13
|
+
* when this path actually runs (mirrors the optional-`ethers` pattern in
|
|
14
|
+
* `signing.ts`). Agents that never select Surplus never load them.
|
|
15
|
+
*
|
|
16
|
+
* @module inference/surplusInference
|
|
17
|
+
*/
|
|
18
|
+
import { readFileSync, writeFileSync } from "node:fs";
|
|
19
|
+
// ── Constants (verified against the x402 directory listing + a live 402 probe, 2026-06-01) ──
|
|
20
|
+
/** Canonical Surplus x402 OpenAI-compatible chat-completions endpoint. */
|
|
21
|
+
export const SURPLUS_URL = "https://www.surplusintelligence.ai/x402/api/inference/v1/chat/completions";
|
|
22
|
+
/** CAIP-2 network id for Base mainnet — the only network Surplus settles on. */
|
|
23
|
+
export const SURPLUS_NETWORK = "eip155:8453";
|
|
24
|
+
/** Canonical Base-mainnet USDC (6 decimals). Settlement asset is asserted against this. */
|
|
25
|
+
export const BASE_USDC = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
26
|
+
/**
|
|
27
|
+
* Canonical Surplus settlement recipient (the x402 `payTo`), from the live 402
|
|
28
|
+
* probe (2026-06-01). Asserted by DEFAULT as defense-in-depth against a spoofed
|
|
29
|
+
* payment recipient. x402 settles to the resource server's single address, so
|
|
30
|
+
* one value is correct for the marketplace (peers are paid off-band, not via
|
|
31
|
+
* this `payTo`). Override via {@link SurplusConfig.payTo} if Surplus rotates it.
|
|
32
|
+
*/
|
|
33
|
+
export const SURPLUS_PAYTO = "0x8581784D3E598cCa3482375CFF2409Ac9DD8c402";
|
|
34
|
+
/** Default Surplus model when none is configured (cheap, broadly available). */
|
|
35
|
+
export const DEFAULT_SURPLUS_MODEL = "llama-3.3-70b";
|
|
36
|
+
/**
|
|
37
|
+
* Default per-call USDC ceiling. A safety backstop against a malicious or
|
|
38
|
+
* mispriced 402 quote — generous enough not to block normal frontier calls
|
|
39
|
+
* (starting price ≈ $0.0033/call), tight enough to bound a runaway/spoofed
|
|
40
|
+
* quote. Override via {@link SurplusConfig.maxUsdcPerCall}.
|
|
41
|
+
*/
|
|
42
|
+
export const DEFAULT_MAX_USDC_PER_CALL = 1.0;
|
|
43
|
+
/**
|
|
44
|
+
* Default AGGREGATE USDC ceiling per rolling window, applied when no
|
|
45
|
+
* `maxUsdcPerSession` is configured. A long-running autonomous/mining daemon is
|
|
46
|
+
* otherwise bounded only by the per-call cap — this is the one control that
|
|
47
|
+
* bounds TOTAL spend. Generous (~3,000 calls at the ≈$0.0033 starting price) so
|
|
48
|
+
* it never halts normal use, tight enough to stop a runaway. Raise it via
|
|
49
|
+
* {@link SurplusConfig.maxUsdcPerSession} for heavier workloads.
|
|
50
|
+
*/
|
|
51
|
+
export const DEFAULT_MAX_USDC_PER_SESSION = 10.0;
|
|
52
|
+
/**
|
|
53
|
+
* Rolling-window length for the per-session cap (24h). When the window elapses
|
|
54
|
+
* the accumulator resets, so the cap behaves as a per-day budget rather than a
|
|
55
|
+
* lifetime one — a persisted tracker won't permanently brick an agent. Mirrors
|
|
56
|
+
* the daily-cap idiom used elsewhere in the protocol.
|
|
57
|
+
*/
|
|
58
|
+
const SURPLUS_SESSION_WINDOW_MS = 24 * 60 * 60 * 1000;
|
|
59
|
+
/** USDC has 6 decimals — multiplier from human units to atomic base units. */
|
|
60
|
+
const USDC_DECIMALS = 6;
|
|
61
|
+
const USDC_BASE = 10n ** BigInt(USDC_DECIMALS);
|
|
62
|
+
/**
|
|
63
|
+
* Dynamically import an OPTIONAL peer via a NON-LITERAL specifier. The
|
|
64
|
+
* non-literal specifier makes `tsc` return `Promise<any>` and skip type
|
|
65
|
+
* resolution — `@x402/fetch` ships its types behind an `exports` map that the
|
|
66
|
+
* runtime's `moduleResolution: node` can't follow, and we cast against the
|
|
67
|
+
* hand-verified API anyway (mirrors the optional-`ethers` pattern in
|
|
68
|
+
* `signing.ts`).
|
|
69
|
+
*/
|
|
70
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
71
|
+
async function importOptional(specifier) {
|
|
72
|
+
return import(specifier);
|
|
73
|
+
}
|
|
74
|
+
/** True if a thrown error is a "module not installed" failure (ESM or CJS code). */
|
|
75
|
+
function isModuleNotFound(err) {
|
|
76
|
+
if (!err || typeof err !== "object" || !("code" in err))
|
|
77
|
+
return false;
|
|
78
|
+
const code = err.code;
|
|
79
|
+
return code === "ERR_MODULE_NOT_FOUND" || code === "MODULE_NOT_FOUND";
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Walk an error's `cause` chain for a {@link SurplusError}. An x402 transport
|
|
83
|
+
* may re-wrap a selector throw (`over_cap` / `payto_mismatch` / …) in its own
|
|
84
|
+
* error type; this recovers the original typed code so it survives rather than
|
|
85
|
+
* degrading to a generic `request_failed` (mirrors the Python
|
|
86
|
+
* `_find_surplus_error`).
|
|
87
|
+
*/
|
|
88
|
+
function findSurplusError(err) {
|
|
89
|
+
let cur = err;
|
|
90
|
+
const seen = new Set();
|
|
91
|
+
for (let i = 0; i < 8; i++) {
|
|
92
|
+
if (cur == null || seen.has(cur))
|
|
93
|
+
break;
|
|
94
|
+
seen.add(cur);
|
|
95
|
+
if (cur instanceof SurplusError)
|
|
96
|
+
return cur;
|
|
97
|
+
cur = cur.cause;
|
|
98
|
+
}
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Typed error for every Surplus failure mode. Thrown — never swallowed into a
|
|
103
|
+
* silent fallback (default-restrictive: an error must never become an
|
|
104
|
+
* unintended spend on another rail).
|
|
105
|
+
*/
|
|
106
|
+
export class SurplusError extends Error {
|
|
107
|
+
/** Stable machine code for programmatic handling / tests. */
|
|
108
|
+
code;
|
|
109
|
+
constructor(message, code = "surplus_error") {
|
|
110
|
+
super(message);
|
|
111
|
+
this.name = "SurplusError";
|
|
112
|
+
this.code = code;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/** Atomic USDC base units of a requirement (v2 `amount`, fallback v1 `maxAmountRequired`). */
|
|
116
|
+
function requirementBaseUnits(req) {
|
|
117
|
+
const raw = req.amount ?? req.maxAmountRequired;
|
|
118
|
+
if (raw == null) {
|
|
119
|
+
throw new SurplusError("Surplus 402 quote is missing an amount field.", "bad_quote");
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
return BigInt(raw);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
throw new SurplusError(`Surplus 402 quote has a non-integer amount: ${String(raw)}`, "bad_quote");
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/** Format atomic USDC base units as a human "$X.XXXXXX" string for error messages. */
|
|
129
|
+
function formatUsdc(baseUnits) {
|
|
130
|
+
const whole = baseUnits / USDC_BASE;
|
|
131
|
+
const frac = (baseUnits % USDC_BASE).toString().padStart(USDC_DECIMALS, "0");
|
|
132
|
+
return `${whole}.${frac}`;
|
|
133
|
+
}
|
|
134
|
+
/** Convert a human USDC ceiling (e.g. 1.0) to atomic base units. */
|
|
135
|
+
export function usdcToBaseUnits(usdc) {
|
|
136
|
+
if (!Number.isFinite(usdc) || usdc < 0) {
|
|
137
|
+
throw new SurplusError(`Invalid maxUsdcPerCall: ${String(usdc)}`, "bad_config");
|
|
138
|
+
}
|
|
139
|
+
// Round to the nearest atomic unit; avoids float drift on values like 0.1.
|
|
140
|
+
return BigInt(Math.round(usdc * Number(USDC_BASE)));
|
|
141
|
+
}
|
|
142
|
+
/** Read-only clock seam (kept tiny so the selector stays easy to reason about). */
|
|
143
|
+
function nowMs() {
|
|
144
|
+
return Date.now();
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Load a persisted spend state, rolling the window over if it has elapsed (a
|
|
148
|
+
* restart after the window means a fresh budget). Any read/parse failure starts
|
|
149
|
+
* fresh — persistence is best-effort and never blocks inference.
|
|
150
|
+
*/
|
|
151
|
+
function loadSpendState(statePath) {
|
|
152
|
+
const now = nowMs();
|
|
153
|
+
try {
|
|
154
|
+
const obj = JSON.parse(readFileSync(statePath, "utf8"));
|
|
155
|
+
const windowStartMs = typeof obj.windowStartMs === "number" ? obj.windowStartMs : now;
|
|
156
|
+
if (now - windowStartMs >= SURPLUS_SESSION_WINDOW_MS) {
|
|
157
|
+
return { spentBaseUnits: 0n, windowStartMs: now };
|
|
158
|
+
}
|
|
159
|
+
let spent = 0n;
|
|
160
|
+
try {
|
|
161
|
+
spent = BigInt(String(obj.spentBaseUnits));
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
spent = 0n;
|
|
165
|
+
}
|
|
166
|
+
return { spentBaseUnits: spent < 0n ? 0n : spent, windowStartMs };
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
return { spentBaseUnits: 0n, windowStartMs: now };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
/** Persist the tracker (best-effort — a write failure never blocks inference). */
|
|
173
|
+
function saveSpendState(statePath, tracker) {
|
|
174
|
+
try {
|
|
175
|
+
writeFileSync(statePath, JSON.stringify({
|
|
176
|
+
spentBaseUnits: tracker.spentBaseUnits.toString(),
|
|
177
|
+
windowStartMs: tracker.windowStartMs,
|
|
178
|
+
}), "utf8");
|
|
179
|
+
}
|
|
180
|
+
catch {
|
|
181
|
+
/* best-effort: never block a paid inference on a persistence failure */
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Build a spend tracker for a runtime/session. With no `statePath` it is a fresh
|
|
186
|
+
* in-memory accumulator; with one it loads the prior cumulative spend and
|
|
187
|
+
* persists after each authorized spend so the cap survives restarts.
|
|
188
|
+
*/
|
|
189
|
+
export function createSurplusSpendTracker(statePath) {
|
|
190
|
+
if (!statePath) {
|
|
191
|
+
return { spentBaseUnits: 0n, windowStartMs: nowMs() };
|
|
192
|
+
}
|
|
193
|
+
const loaded = loadSpendState(statePath);
|
|
194
|
+
const tracker = {
|
|
195
|
+
spentBaseUnits: loaded.spentBaseUnits,
|
|
196
|
+
windowStartMs: loaded.windowStartMs,
|
|
197
|
+
};
|
|
198
|
+
tracker.persist = () => saveSpendState(statePath, tracker);
|
|
199
|
+
return tracker;
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Resolve the effective `payTo` assertion target: an explicit config override,
|
|
203
|
+
* else the canonical {@link SURPLUS_PAYTO}. The recipient is asserted by default
|
|
204
|
+
* (defense-in-depth) — unlike {@link makeSurplusSelector}, which only asserts
|
|
205
|
+
* when given a `payTo` (so its unit-level "open selector" semantics are kept).
|
|
206
|
+
*/
|
|
207
|
+
export function resolveSurplusPayTo(config) {
|
|
208
|
+
return config?.payTo ?? SURPLUS_PAYTO;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Build the payment-requirements selector enforcing the spend controls:
|
|
212
|
+
* - scheme must be `exact`, on Base (`eip155:8453`), paying canonical USDC;
|
|
213
|
+
* - if `payTo` is configured, the quote's recipient must match it;
|
|
214
|
+
* - the cheapest eligible quote is chosen, and rejected if it exceeds the
|
|
215
|
+
* per-call cap;
|
|
216
|
+
* - if a `tracker` + `maxSessionBaseUnits` are given, the AUTHORIZED amount is
|
|
217
|
+
* accumulated and rejected once the running total would exceed the session
|
|
218
|
+
* cap. Tracking the authorized (not settled) amount is deliberately
|
|
219
|
+
* conservative: it can only stop spending earlier, never permit an overspend.
|
|
220
|
+
*
|
|
221
|
+
* Any violation throws a {@link SurplusError} (propagates out of the wrapped
|
|
222
|
+
* fetch) — the payment is never signed, and a rejected attempt is NOT counted.
|
|
223
|
+
*/
|
|
224
|
+
export function makeSurplusSelector(opts) {
|
|
225
|
+
const wantAsset = (opts.asset ?? BASE_USDC).toLowerCase();
|
|
226
|
+
const wantPayTo = opts.payTo?.toLowerCase();
|
|
227
|
+
return (_x402Version, requirements) => {
|
|
228
|
+
const eligible = requirements.filter((r) => r.scheme === "exact" &&
|
|
229
|
+
String(r.network) === SURPLUS_NETWORK &&
|
|
230
|
+
typeof r.asset === "string" &&
|
|
231
|
+
r.asset.toLowerCase() === wantAsset);
|
|
232
|
+
if (eligible.length === 0) {
|
|
233
|
+
throw new SurplusError("Surplus offered no `exact` USDC payment option on Base (eip155:8453).", "no_eligible_requirement");
|
|
234
|
+
}
|
|
235
|
+
// Cheapest first — defends against an inflated duplicate quote.
|
|
236
|
+
eligible.sort((a, b) => {
|
|
237
|
+
const av = requirementBaseUnits(a);
|
|
238
|
+
const bv = requirementBaseUnits(b);
|
|
239
|
+
return av < bv ? -1 : av > bv ? 1 : 0;
|
|
240
|
+
});
|
|
241
|
+
const chosen = eligible[0];
|
|
242
|
+
if (wantPayTo &&
|
|
243
|
+
typeof chosen.payTo === "string" &&
|
|
244
|
+
chosen.payTo.toLowerCase() !== wantPayTo) {
|
|
245
|
+
throw new SurplusError(`Surplus payTo ${chosen.payTo} does not match the configured expected address.`, "payto_mismatch");
|
|
246
|
+
}
|
|
247
|
+
const amount = requirementBaseUnits(chosen);
|
|
248
|
+
if (amount > opts.maxBaseUnits) {
|
|
249
|
+
throw new SurplusError(`Surplus quote ${formatUsdc(amount)} USDC exceeds the per-call cap of ` +
|
|
250
|
+
`${formatUsdc(opts.maxBaseUnits)} USDC.`, "over_cap");
|
|
251
|
+
}
|
|
252
|
+
// Aggregate session cap — the one control that bounds TOTAL spend across a
|
|
253
|
+
// long-running daemon / mining loop (the per-call cap does not). Record only
|
|
254
|
+
// on success, so a rejected attempt never inflates the total.
|
|
255
|
+
if (opts.tracker && opts.maxSessionBaseUnits != null) {
|
|
256
|
+
const now = nowMs();
|
|
257
|
+
// Roll the window over once it has elapsed (covers both a >24h continuous
|
|
258
|
+
// run and a restart that resumed a still-open window from disk). Seed the
|
|
259
|
+
// window start on first use for trackers created without one.
|
|
260
|
+
if (opts.tracker.windowStartMs &&
|
|
261
|
+
now - opts.tracker.windowStartMs >= SURPLUS_SESSION_WINDOW_MS) {
|
|
262
|
+
opts.tracker.windowStartMs = now;
|
|
263
|
+
opts.tracker.spentBaseUnits = 0n;
|
|
264
|
+
}
|
|
265
|
+
else if (!opts.tracker.windowStartMs) {
|
|
266
|
+
opts.tracker.windowStartMs = now;
|
|
267
|
+
}
|
|
268
|
+
const projected = opts.tracker.spentBaseUnits + amount;
|
|
269
|
+
if (projected > opts.maxSessionBaseUnits) {
|
|
270
|
+
throw new SurplusError(`Surplus session spend ${formatUsdc(projected)} USDC would exceed the ` +
|
|
271
|
+
`per-session cap of ${formatUsdc(opts.maxSessionBaseUnits)} USDC.`, "over_session_cap");
|
|
272
|
+
}
|
|
273
|
+
opts.tracker.spentBaseUnits = projected;
|
|
274
|
+
opts.tracker.persist?.();
|
|
275
|
+
}
|
|
276
|
+
return chosen;
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Map runtime messages + options to an OpenAI-compatible chat-completions body.
|
|
281
|
+
* `systemPrompt` is prepended as a `system` message (the runtime convention).
|
|
282
|
+
* Streaming is forced off — v1 is non-streaming.
|
|
283
|
+
*/
|
|
284
|
+
export function buildSurplusBody(messages, options, model) {
|
|
285
|
+
const out = [];
|
|
286
|
+
if (options?.systemPrompt) {
|
|
287
|
+
out.push({ role: "system", content: options.systemPrompt });
|
|
288
|
+
}
|
|
289
|
+
for (const m of messages) {
|
|
290
|
+
const om = { role: m.role, content: m.content };
|
|
291
|
+
// Pass through tool-call linkage when present; harmless for prose agents.
|
|
292
|
+
if (m.role === "tool" && m.toolCallId)
|
|
293
|
+
om.tool_call_id = m.toolCallId;
|
|
294
|
+
out.push(om);
|
|
295
|
+
}
|
|
296
|
+
const body = { model, messages: out, stream: false };
|
|
297
|
+
if (options?.maxTokens != null)
|
|
298
|
+
body.max_tokens = options.maxTokens;
|
|
299
|
+
if (options?.temperature != null)
|
|
300
|
+
body.temperature = options.temperature;
|
|
301
|
+
return body;
|
|
302
|
+
}
|
|
303
|
+
function toCount(v) {
|
|
304
|
+
return typeof v === "number" && Number.isFinite(v) ? v : 0;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Decode the `X-PAYMENT-RESPONSE` settlement header (x402 encodes it as
|
|
308
|
+
* base64(JSON); base64url is tolerated). Returns `null` on any failure — the
|
|
309
|
+
* settlement check is best-effort and never blocks a successful 2xx body.
|
|
310
|
+
*/
|
|
311
|
+
function decodeSettleHeader(header) {
|
|
312
|
+
try {
|
|
313
|
+
const normalized = header.replace(/-/g, "+").replace(/_/g, "/");
|
|
314
|
+
return JSON.parse(Buffer.from(normalized, "base64").toString("utf8"));
|
|
315
|
+
}
|
|
316
|
+
catch {
|
|
317
|
+
return null;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Parse a Surplus chat-completions Response into an {@link InferenceResult}.
|
|
322
|
+
*
|
|
323
|
+
* Also performs a defensive settlement check: if the `X-PAYMENT-RESPONSE`
|
|
324
|
+
* header decodes to an explicit failure, we surface it rather than returning a
|
|
325
|
+
* body that was not actually paid for. `creditsCost` is always 0 — these calls
|
|
326
|
+
* are paid in USDC, never in Nookplot credits.
|
|
327
|
+
*/
|
|
328
|
+
export async function parseSurplusResponse(res, requestedModel) {
|
|
329
|
+
if (!res.ok) {
|
|
330
|
+
let detail = "";
|
|
331
|
+
try {
|
|
332
|
+
detail = (await res.text()).slice(0, 500);
|
|
333
|
+
}
|
|
334
|
+
catch {
|
|
335
|
+
/* ignore body read failure */
|
|
336
|
+
}
|
|
337
|
+
throw new SurplusError(`Surplus returned HTTP ${res.status}${detail ? `: ${detail}` : ""}`, res.status === 402 ? "payment_required" : "http_error");
|
|
338
|
+
}
|
|
339
|
+
// Defensive settlement true-up (best-effort): if the settlement header
|
|
340
|
+
// decodes to an explicit failure, surface it rather than returning a body
|
|
341
|
+
// that was not actually paid for.
|
|
342
|
+
const settleHeader = res.headers.get("X-PAYMENT-RESPONSE");
|
|
343
|
+
if (settleHeader) {
|
|
344
|
+
const settle = decodeSettleHeader(settleHeader);
|
|
345
|
+
if (settle && settle.success === false) {
|
|
346
|
+
throw new SurplusError(`Surplus payment did not settle: ${settle.errorReason ?? settle.errorMessage ?? "unknown reason"}`, "settle_failed");
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
let json;
|
|
350
|
+
try {
|
|
351
|
+
json = await res.json();
|
|
352
|
+
}
|
|
353
|
+
catch {
|
|
354
|
+
throw new SurplusError("Surplus returned a non-JSON response body.", "bad_response");
|
|
355
|
+
}
|
|
356
|
+
const obj = (json ?? {});
|
|
357
|
+
const content = obj.choices?.[0]?.message?.content;
|
|
358
|
+
if (typeof content !== "string") {
|
|
359
|
+
throw new SurplusError("Surplus response is missing choices[0].message.content.", "bad_response");
|
|
360
|
+
}
|
|
361
|
+
const usage = obj.usage ?? {};
|
|
362
|
+
return {
|
|
363
|
+
content,
|
|
364
|
+
model: typeof obj.model === "string" ? obj.model : requestedModel,
|
|
365
|
+
provider: "surplus",
|
|
366
|
+
usage: {
|
|
367
|
+
promptTokens: toCount(usage.prompt_tokens),
|
|
368
|
+
completionTokens: toCount(usage.completion_tokens),
|
|
369
|
+
totalTokens: toCount(usage.total_tokens),
|
|
370
|
+
creditsCost: 0,
|
|
371
|
+
},
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
/** Normalize a raw private key to the `0x`-prefixed form viem expects. */
|
|
375
|
+
function normalizePrivateKey(pk) {
|
|
376
|
+
const hex = pk.startsWith("0x") ? pk : `0x${pk}`;
|
|
377
|
+
return hex;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* Lazily build the x402 payment-wrapped fetch from a raw private key. Imports
|
|
381
|
+
* the optional `@x402/*` + `viem` peers on first use; a missing peer yields a
|
|
382
|
+
* clear install hint. EIP-3009 (`exact`) signing is fully offline — no RPC URL.
|
|
383
|
+
*/
|
|
384
|
+
async function buildPaymentFetch(privateKey, selector) {
|
|
385
|
+
try {
|
|
386
|
+
const viemAccounts = await importOptional("viem/accounts");
|
|
387
|
+
const x402Fetch = await importOptional("@x402/fetch");
|
|
388
|
+
const x402Evm = await importOptional("@x402/evm");
|
|
389
|
+
const account = viemAccounts.privateKeyToAccount(normalizePrivateKey(privateKey));
|
|
390
|
+
// `selector` is structurally an x402 SelectPaymentRequirements; the dynamic
|
|
391
|
+
// imports are untyped (any), so the wiring stays contained to this function.
|
|
392
|
+
const client = new x402Fetch.x402Client(selector).register(SURPLUS_NETWORK, new x402Evm.ExactEvmScheme(account));
|
|
393
|
+
return x402Fetch.wrapFetchWithPayment(globalThis.fetch, client);
|
|
394
|
+
}
|
|
395
|
+
catch (err) {
|
|
396
|
+
if (isModuleNotFound(err)) {
|
|
397
|
+
throw new SurplusError("Surplus inference requires the optional peers '@x402/fetch', '@x402/evm', and 'viem'. " +
|
|
398
|
+
"Install them: npm install @x402/fetch @x402/evm viem", "missing_deps");
|
|
399
|
+
}
|
|
400
|
+
if (err instanceof SurplusError)
|
|
401
|
+
throw err;
|
|
402
|
+
throw new SurplusError(`Failed to initialize the Surplus x402 client: ${err instanceof Error ? err.message : String(err)}`, "client_init_failed");
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
/**
|
|
406
|
+
* Run a single non-streaming inference against Surplus, paying per-request in
|
|
407
|
+
* USDC via x402 from the agent's own wallet. Returns the same
|
|
408
|
+
* {@link InferenceResult} shape as the gateway path, so it slots transparently
|
|
409
|
+
* into every downstream consumer (chat, autonomous loop, mining, goal loop).
|
|
410
|
+
*/
|
|
411
|
+
export async function surplusInference(params) {
|
|
412
|
+
const { privateKey, messages, options, config } = params;
|
|
413
|
+
// Mandatory keyless gate (correctness, not an attack vector): never attempt
|
|
414
|
+
// an unsigned/partial payment. Throw before any network call.
|
|
415
|
+
if (!privateKey) {
|
|
416
|
+
throw new SurplusError("Surplus inference requires a local signing key (set `privateKey`). " +
|
|
417
|
+
"This agent is API-key-only and cannot sign x402 payments.", "keyless");
|
|
418
|
+
}
|
|
419
|
+
const model = options?.model ?? config?.model ?? DEFAULT_SURPLUS_MODEL;
|
|
420
|
+
const url = config?.baseUrl ?? SURPLUS_URL;
|
|
421
|
+
const maxBaseUnits = usdcToBaseUnits(config?.maxUsdcPerCall ?? DEFAULT_MAX_USDC_PER_CALL);
|
|
422
|
+
const selector = makeSurplusSelector({
|
|
423
|
+
maxBaseUnits,
|
|
424
|
+
asset: BASE_USDC,
|
|
425
|
+
payTo: resolveSurplusPayTo(config), // default-on payTo assertion
|
|
426
|
+
tracker: params.tracker,
|
|
427
|
+
// Default-on aggregate cap: a long-running daemon is otherwise bounded only
|
|
428
|
+
// by the per-call cap. Override (incl. a large value to effectively disable)
|
|
429
|
+
// via `maxUsdcPerSession`.
|
|
430
|
+
maxSessionBaseUnits: usdcToBaseUnits(config?.maxUsdcPerSession ?? DEFAULT_MAX_USDC_PER_SESSION),
|
|
431
|
+
});
|
|
432
|
+
const fetchWithPay = params.paymentFetch ?? (await buildPaymentFetch(privateKey, selector));
|
|
433
|
+
const body = buildSurplusBody(messages, options, model);
|
|
434
|
+
let res;
|
|
435
|
+
try {
|
|
436
|
+
res = await fetchWithPay(url, {
|
|
437
|
+
method: "POST",
|
|
438
|
+
headers: { "Content-Type": "application/json" },
|
|
439
|
+
body: JSON.stringify(body),
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
catch (err) {
|
|
443
|
+
if (err instanceof SurplusError)
|
|
444
|
+
throw err; // selector cap/asset/payTo rejection
|
|
445
|
+
// A wrapped selector throw (the transport re-raised it as its own error)
|
|
446
|
+
// still carries the typed code on its cause chain — recover it so the
|
|
447
|
+
// specific reason survives instead of a generic `request_failed`.
|
|
448
|
+
const wrapped = findSurplusError(err);
|
|
449
|
+
if (wrapped)
|
|
450
|
+
throw wrapped;
|
|
451
|
+
throw new SurplusError(`Surplus request failed: ${err instanceof Error ? err.message : String(err)}`, "request_failed");
|
|
452
|
+
}
|
|
453
|
+
return parseSurplusResponse(res, model);
|
|
454
|
+
}
|
|
455
|
+
//# sourceMappingURL=surplusInference.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"surplusInference.js","sourceRoot":"","sources":["../../src/inference/surplusInference.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAStD,+FAA+F;AAE/F,0EAA0E;AAC1E,MAAM,CAAC,MAAM,WAAW,GACtB,2EAA2E,CAAC;AAE9E,gFAAgF;AAChF,MAAM,CAAC,MAAM,eAAe,GAAG,aAAa,CAAC;AAE7C,2FAA2F;AAC3F,MAAM,CAAC,MAAM,SAAS,GAAG,4CAA4C,CAAC;AAEtE;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,4CAA4C,CAAC;AAE1E,gFAAgF;AAChF,MAAM,CAAC,MAAM,qBAAqB,GAAG,eAAe,CAAC;AAErD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAC;AAE7C;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,4BAA4B,GAAG,IAAI,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,yBAAyB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;AAEtD,8EAA8E;AAC9E,MAAM,aAAa,GAAG,CAAC,CAAC;AACxB,MAAM,SAAS,GAAG,GAAG,IAAI,MAAM,CAAC,aAAa,CAAC,CAAC;AAE/C;;;;;;;GAOG;AACH,8DAA8D;AAC9D,KAAK,UAAU,cAAc,CAAC,SAAiB;IAC7C,OAAO,MAAM,CAAC,SAAS,CAAC,CAAC;AAC3B,CAAC;AAED,oFAAoF;AACpF,SAAS,gBAAgB,CAAC,GAAY;IACpC,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,CAAC,MAAM,IAAI,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACtE,MAAM,IAAI,GAAI,GAAyB,CAAC,IAAI,CAAC;IAC7C,OAAO,IAAI,KAAK,sBAAsB,IAAI,IAAI,KAAK,kBAAkB,CAAC;AACxE,CAAC;AAED;;;;;;GAMG;AACH,SAAS,gBAAgB,CAAC,GAAY;IACpC,IAAI,GAAG,GAAY,GAAG,CAAC;IACvB,MAAM,IAAI,GAAG,IAAI,GAAG,EAAW,CAAC;IAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QAC3B,IAAI,GAAG,IAAI,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,MAAM;QACxC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACd,IAAI,GAAG,YAAY,YAAY;YAAE,OAAO,GAAG,CAAC;QAC5C,GAAG,GAAI,GAA2B,CAAC,KAAK,CAAC;IAC3C,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;;GAIG;AACH,MAAM,OAAO,YAAa,SAAQ,KAAK;IACrC,6DAA6D;IACpD,IAAI,CAAS;IACtB,YAAY,OAAe,EAAE,IAAI,GAAG,eAAe;QACjD,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,cAAc,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,CAAC;CACF;AAwBD,8FAA8F;AAC9F,SAAS,oBAAoB,CAAC,GAA8B;IAC1D,MAAM,GAAG,GAAG,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,iBAAiB,CAAC;IAChD,IAAI,GAAG,IAAI,IAAI,EAAE,CAAC;QAChB,MAAM,IAAI,YAAY,CACpB,+CAA+C,EAC/C,WAAW,CACZ,CAAC;IACJ,CAAC;IACD,IAAI,CAAC;QACH,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,YAAY,CACpB,+CAA+C,MAAM,CAAC,GAAG,CAAC,EAAE,EAC5D,WAAW,CACZ,CAAC;IACJ,CAAC;AACH,CAAC;AAED,sFAAsF;AACtF,SAAS,UAAU,CAAC,SAAiB;IACnC,MAAM,KAAK,GAAG,SAAS,GAAG,SAAS,CAAC;IACpC,MAAM,IAAI,GAAG,CAAC,SAAS,GAAG,SAAS,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,aAAa,EAAE,GAAG,CAAC,CAAC;IAC7E,OAAO,GAAG,KAAK,IAAI,IAAI,EAAE,CAAC;AAC5B,CAAC;AAED,oEAAoE;AACpE,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,YAAY,CACpB,2BAA2B,MAAM,CAAC,IAAI,CAAC,EAAE,EACzC,YAAY,CACb,CAAC;IACJ,CAAC;IACD,2EAA2E;IAC3E,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;AACtD,CAAC;AAmBD,mFAAmF;AACnF,SAAS,KAAK;IACZ,OAAO,IAAI,CAAC,GAAG,EAAE,CAAC;AACpB,CAAC;AAED;;;;GAIG;AACH,SAAS,cAAc,CAAC,SAAiB;IAIvC,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC;IACpB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAGrD,CAAC;QACF,MAAM,aAAa,GACjB,OAAO,GAAG,CAAC,aAAa,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC;QAClE,IAAI,GAAG,GAAG,aAAa,IAAI,yBAAyB,EAAE,CAAC;YACrD,OAAO,EAAE,cAAc,EAAE,EAAE,EAAE,aAAa,EAAE,GAAG,EAAE,CAAC;QACpD,CAAC;QACD,IAAI,KAAK,GAAG,EAAE,CAAC;QACf,IAAI,CAAC;YACH,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC;QAC7C,CAAC;QAAC,MAAM,CAAC;YACP,KAAK,GAAG,EAAE,CAAC;QACb,CAAC;QACD,OAAO,EAAE,cAAc,EAAE,KAAK,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,aAAa,EAAE,CAAC;IACpE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,cAAc,EAAE,EAAE,EAAE,aAAa,EAAE,GAAG,EAAE,CAAC;IACpD,CAAC;AACH,CAAC;AAED,kFAAkF;AAClF,SAAS,cAAc,CAAC,SAAiB,EAAE,OAA4B;IACrE,IAAI,CAAC;QACH,aAAa,CACX,SAAS,EACT,IAAI,CAAC,SAAS,CAAC;YACb,cAAc,EAAE,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE;YACjD,aAAa,EAAE,OAAO,CAAC,aAAa;SACrC,CAAC,EACF,MAAM,CACP,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,wEAAwE;IAC1E,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CACvC,SAAkB;IAElB,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,EAAE,cAAc,EAAE,EAAE,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE,CAAC;IACxD,CAAC;IACD,MAAM,MAAM,GAAG,cAAc,CAAC,SAAS,CAAC,CAAC;IACzC,MAAM,OAAO,GAAwB;QACnC,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,aAAa,EAAE,MAAM,CAAC,aAAa;KACpC,CAAC;IACF,OAAO,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC,cAAc,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC3D,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAAC,MAAsB;IACxD,OAAO,MAAM,EAAE,KAAK,IAAI,aAAa,CAAC;AACxC,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAMnC;IACC,MAAM,SAAS,GAAG,CAAC,IAAI,CAAC,KAAK,IAAI,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;IAC1D,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,EAAE,WAAW,EAAE,CAAC;IAE5C,OAAO,CAAC,YAAY,EAAE,YAAY,EAAE,EAAE;QACpC,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,CAClC,CAAC,CAAC,EAAE,EAAE,CACJ,CAAC,CAAC,MAAM,KAAK,OAAO;YACpB,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,eAAe;YACrC,OAAO,CAAC,CAAC,KAAK,KAAK,QAAQ;YAC3B,CAAC,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,SAAS,CACtC,CAAC;QACF,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,YAAY,CACpB,uEAAuE,EACvE,yBAAyB,CAC1B,CAAC;QACJ,CAAC;QAED,gEAAgE;QAChE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;YACrB,MAAM,EAAE,GAAG,oBAAoB,CAAC,CAAC,CAAC,CAAC;YACnC,MAAM,EAAE,GAAG,oBAAoB,CAAC,CAAC,CAAC,CAAC;YACnC,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACxC,CAAC,CAAC,CAAC;QACH,MAAM,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;QAE3B,IACE,SAAS;YACT,OAAO,MAAM,CAAC,KAAK,KAAK,QAAQ;YAChC,MAAM,CAAC,KAAK,CAAC,WAAW,EAAE,KAAK,SAAS,EACxC,CAAC;YACD,MAAM,IAAI,YAAY,CACpB,iBAAiB,MAAM,CAAC,KAAK,kDAAkD,EAC/E,gBAAgB,CACjB,CAAC;QACJ,CAAC;QAED,MAAM,MAAM,GAAG,oBAAoB,CAAC,MAAM,CAAC,CAAC;QAC5C,IAAI,MAAM,GAAG,IAAI,CAAC,YAAY,EAAE,CAAC;YAC/B,MAAM,IAAI,YAAY,CACpB,iBAAiB,UAAU,CAAC,MAAM,CAAC,oCAAoC;gBACrE,GAAG,UAAU,CAAC,IAAI,CAAC,YAAY,CAAC,QAAQ,EAC1C,UAAU,CACX,CAAC;QACJ,CAAC;QAED,2EAA2E;QAC3E,6EAA6E;QAC7E,8DAA8D;QAC9D,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,mBAAmB,IAAI,IAAI,EAAE,CAAC;YACrD,MAAM,GAAG,GAAG,KAAK,EAAE,CAAC;YACpB,0EAA0E;YAC1E,0EAA0E;YAC1E,8DAA8D;YAC9D,IACE,IAAI,CAAC,OAAO,CAAC,aAAa;gBAC1B,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,IAAI,yBAAyB,EAC7D,CAAC;gBACD,IAAI,CAAC,OAAO,CAAC,aAAa,GAAG,GAAG,CAAC;gBACjC,IAAI,CAAC,OAAO,CAAC,cAAc,GAAG,EAAE,CAAC;YACnC,CAAC;iBAAM,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;gBACvC,IAAI,CAAC,OAAO,CAAC,aAAa,GAAG,GAAG,CAAC;YACnC,CAAC;YACD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,GAAG,MAAM,CAAC;YACvD,IAAI,SAAS,GAAG,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBACzC,MAAM,IAAI,YAAY,CACpB,yBAAyB,UAAU,CAAC,SAAS,CAAC,yBAAyB;oBACrE,sBAAsB,UAAU,CAAC,IAAI,CAAC,mBAAmB,CAAC,QAAQ,EACpE,kBAAkB,CACnB,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,OAAO,CAAC,cAAc,GAAG,SAAS,CAAC;YACxC,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC;QAC3B,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,QAA4B,EAC5B,OAAqC,EACrC,KAAa;IAEb,MAAM,GAAG,GAAmC,EAAE,CAAC;IAC/C,IAAI,OAAO,EAAE,YAAY,EAAE,CAAC;QAC1B,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;IAC9D,CAAC;IACD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,MAAM,EAAE,GAA4B,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC;QACzE,0EAA0E;QAC1E,IAAI,CAAC,CAAC,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,UAAU;YAAE,EAAE,CAAC,YAAY,GAAG,CAAC,CAAC,UAAU,CAAC;QACtE,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACf,CAAC;IAED,MAAM,IAAI,GAA4B,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAC9E,IAAI,OAAO,EAAE,SAAS,IAAI,IAAI;QAAE,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC;IACpE,IAAI,OAAO,EAAE,WAAW,IAAI,IAAI;QAAE,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC;IACzE,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,OAAO,CAAC,CAAU;IACzB,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED;;;;GAIG;AACH,SAAS,kBAAkB,CACzB,MAAc;IAEd,IAAI,CAAC;QACH,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAChE,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,GAAa,EACb,cAAsB;IAEtB,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,CAAC;YACH,MAAM,GAAG,CAAC,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC5C,CAAC;QAAC,MAAM,CAAC;YACP,8BAA8B;QAChC,CAAC;QACD,MAAM,IAAI,YAAY,CACpB,yBAAyB,GAAG,CAAC,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EACnE,GAAG,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,YAAY,CACvD,CAAC;IACJ,CAAC;IAED,uEAAuE;IACvE,0EAA0E;IAC1E,kCAAkC;IAClC,MAAM,YAAY,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,CAAC;IAC3D,IAAI,YAAY,EAAE,CAAC;QACjB,MAAM,MAAM,GAAG,kBAAkB,CAAC,YAAY,CAAC,CAAC;QAChD,IAAI,MAAM,IAAI,MAAM,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;YACvC,MAAM,IAAI,YAAY,CACpB,mCAAmC,MAAM,CAAC,WAAW,IAAI,MAAM,CAAC,YAAY,IAAI,gBAAgB,EAAE,EAClG,eAAe,CAChB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,IAAI,IAAa,CAAC;IAClB,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,YAAY,CACpB,4CAA4C,EAC5C,cAAc,CACf,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,CAAC,IAAI,IAAI,EAAE,CAQtB,CAAC;IACF,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC;IACnD,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAChC,MAAM,IAAI,YAAY,CACpB,yDAAyD,EACzD,cAAc,CACf,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;IAC9B,OAAO;QACL,OAAO;QACP,KAAK,EAAE,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,cAAc;QACjE,QAAQ,EAAE,SAAS;QACnB,KAAK,EAAE;YACL,YAAY,EAAE,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC;YAC1C,gBAAgB,EAAE,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC;YAClD,WAAW,EAAE,OAAO,CAAC,KAAK,CAAC,YAAY,CAAC;YACxC,WAAW,EAAE,CAAC;SACf;KACF,CAAC;AACJ,CAAC;AAED,0EAA0E;AAC1E,SAAS,mBAAmB,CAAC,EAAU;IACrC,MAAM,GAAG,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC;IACjD,OAAO,GAAoB,CAAC;AAC9B,CAAC;AAED;;;;GAIG;AACH,KAAK,UAAU,iBAAiB,CAC9B,UAAkB,EAClB,QAAyB;IAEzB,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,MAAM,cAAc,CAAC,eAAe,CAAC,CAAC;QAC3D,MAAM,SAAS,GAAG,MAAM,cAAc,CAAC,aAAa,CAAC,CAAC;QACtD,MAAM,OAAO,GAAG,MAAM,cAAc,CAAC,WAAW,CAAC,CAAC;QAElD,MAAM,OAAO,GAAG,YAAY,CAAC,mBAAmB,CAC9C,mBAAmB,CAAC,UAAU,CAAC,CAChC,CAAC;QACF,4EAA4E;QAC5E,6EAA6E;QAC7E,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC,QAAQ,CACxD,eAAe,EACf,IAAI,OAAO,CAAC,cAAc,CAAC,OAAO,CAAC,CACpC,CAAC;QACF,OAAO,SAAS,CAAC,oBAAoB,CAAC,UAAU,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IAClE,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,YAAY,CACpB,wFAAwF;gBACtF,sDAAsD,EACxD,cAAc,CACf,CAAC;QACJ,CAAC;QACD,IAAI,GAAG,YAAY,YAAY;YAAE,MAAM,GAAG,CAAC;QAC3C,MAAM,IAAI,YAAY,CACpB,iDAAiD,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EACnG,oBAAoB,CACrB,CAAC;IACJ,CAAC;AACH,CAAC;AAsBD;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,MAA8B;IAE9B,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAEzD,4EAA4E;IAC5E,8DAA8D;IAC9D,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,MAAM,IAAI,YAAY,CACpB,qEAAqE;YACnE,2DAA2D,EAC7D,SAAS,CACV,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,IAAI,MAAM,EAAE,KAAK,IAAI,qBAAqB,CAAC;IACvE,MAAM,GAAG,GAAG,MAAM,EAAE,OAAO,IAAI,WAAW,CAAC;IAC3C,MAAM,YAAY,GAAG,eAAe,CAClC,MAAM,EAAE,cAAc,IAAI,yBAAyB,CACpD,CAAC;IACF,MAAM,QAAQ,GAAG,mBAAmB,CAAC;QACnC,YAAY;QACZ,KAAK,EAAE,SAAS;QAChB,KAAK,EAAE,mBAAmB,CAAC,MAAM,CAAC,EAAE,6BAA6B;QACjE,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,4EAA4E;QAC5E,6EAA6E;QAC7E,2BAA2B;QAC3B,mBAAmB,EAAE,eAAe,CAClC,MAAM,EAAE,iBAAiB,IAAI,4BAA4B,CAC1D;KACF,CAAC,CAAC;IAEH,MAAM,YAAY,GAChB,MAAM,CAAC,YAAY,IAAI,CAAC,MAAM,iBAAiB,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;IACzE,MAAM,IAAI,GAAG,gBAAgB,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IAExD,IAAI,GAAa,CAAC;IAClB,IAAI,CAAC;QACH,GAAG,GAAG,MAAM,YAAY,CAAC,GAAG,EAAE;YAC5B,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;SAC3B,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,GAAG,YAAY,YAAY;YAAE,MAAM,GAAG,CAAC,CAAC,qCAAqC;QACjF,yEAAyE;QACzE,sEAAsE;QACtE,kEAAkE;QAClE,MAAM,OAAO,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACtC,IAAI,OAAO;YAAE,MAAM,OAAO,CAAC;QAC3B,MAAM,IAAI,YAAY,CACpB,2BAA2B,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EAC7E,gBAAgB,CACjB,CAAC;IACJ,CAAC;IAED,OAAO,oBAAoB,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;AAC1C,CAAC"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profile-aware credential loading for @nookplot/runtime.
|
|
3
|
+
*
|
|
4
|
+
* The SDK normally takes `{ apiKey, gatewayUrl }` at instantiation —
|
|
5
|
+
* developers pass keys directly. This helper is for developers who want
|
|
6
|
+
* the SDK to read from `~/.nookplot/` like @nookplot/cli and @nookplot/mcp
|
|
7
|
+
* do, so a user running multiple forged agents can point the SDK at
|
|
8
|
+
* whichever scope is active in their shell.
|
|
9
|
+
*
|
|
10
|
+
* Reads from the same files the rest of the Nookplot stack uses:
|
|
11
|
+
* ~/.nookplot/credentials.json ← creator API key (shared)
|
|
12
|
+
* ~/.nookplot/profiles/<name>/profile.json ← per-agent scope
|
|
13
|
+
* ~/.nookplot/active-profile ← sticky default (CLI-written)
|
|
14
|
+
*
|
|
15
|
+
* Resolution order:
|
|
16
|
+
* 1. Explicit `name` argument to loadProfile()
|
|
17
|
+
* 2. NOOKPLOT_PROFILE env var
|
|
18
|
+
* 3. Sticky default from ~/.nookplot/active-profile
|
|
19
|
+
* 4. null (load default creds only, no scope)
|
|
20
|
+
*
|
|
21
|
+
* @module loadProfile
|
|
22
|
+
*
|
|
23
|
+
* @example Basic usage — pick up whatever profile is active
|
|
24
|
+
* ```ts
|
|
25
|
+
* import { NookplotRuntime } from "@nookplot/runtime";
|
|
26
|
+
* import { loadProfile } from "@nookplot/runtime/loadProfile";
|
|
27
|
+
*
|
|
28
|
+
* const creds = loadProfile(); // respects NOOKPLOT_PROFILE env + sticky default
|
|
29
|
+
* if (!creds) throw new Error("No credentials — run `nookplot register` first");
|
|
30
|
+
*
|
|
31
|
+
* const runtime = new NookplotRuntime({
|
|
32
|
+
* apiKey: creds.apiKey,
|
|
33
|
+
* gatewayUrl: creds.gatewayUrl,
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @example Explicit profile override
|
|
38
|
+
* ```ts
|
|
39
|
+
* const creds = loadProfile("jeffs-researcher");
|
|
40
|
+
* ```
|
|
41
|
+
*
|
|
42
|
+
* @example Multi-agent — run two SDK instances in parallel
|
|
43
|
+
* ```ts
|
|
44
|
+
* const researcherCreds = loadProfile("jeffs-researcher");
|
|
45
|
+
* const writerCreds = loadProfile("jeffs-writer");
|
|
46
|
+
*
|
|
47
|
+
* const researcher = new NookplotRuntime({ apiKey: researcherCreds!.apiKey, ... });
|
|
48
|
+
* const writer = new NookplotRuntime({ apiKey: writerCreds!.apiKey, ... });
|
|
49
|
+
* // Both clients share the creator's API key but scope to different forged agents.
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
export interface LoadedProfile {
|
|
53
|
+
/** Creator's API key (shared across all forged agents owned by this creator). */
|
|
54
|
+
apiKey: string;
|
|
55
|
+
/** Creator's wallet address. */
|
|
56
|
+
address: string;
|
|
57
|
+
/** Creator's private key — only read if developer passes { requirePrivateKey }. */
|
|
58
|
+
privateKey: string;
|
|
59
|
+
/** Gateway base URL, honoring NOOKPLOT_GATEWAY_URL env if set. */
|
|
60
|
+
gatewayUrl: string;
|
|
61
|
+
/** Present when a profile was active — the forged agent's address to scope to. */
|
|
62
|
+
scopedAgentAddress?: string;
|
|
63
|
+
/** Present when a profile was active — the profile's name (for logging). */
|
|
64
|
+
profileName?: string;
|
|
65
|
+
/** Display name of the creator (not the scoped forged agent). */
|
|
66
|
+
displayName?: string;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Resolve the active profile name in priority order.
|
|
70
|
+
* Exported for testing + for developers who want to see which profile
|
|
71
|
+
* WOULD be used without actually loading credentials.
|
|
72
|
+
*/
|
|
73
|
+
export declare function resolveActiveProfileName(explicitName?: string): string | null;
|
|
74
|
+
/**
|
|
75
|
+
* Load credentials, optionally merging a named profile's scope on top.
|
|
76
|
+
*
|
|
77
|
+
* Returns null if no `credentials.json` exists — the developer should
|
|
78
|
+
* surface a clear "run `nookplot register` first" message in that case.
|
|
79
|
+
*
|
|
80
|
+
* Does not throw on invalid profiles — if the named profile doesn't
|
|
81
|
+
* exist or is malformed, falls back to plain creator-direct credentials
|
|
82
|
+
* (matches @nookplot/mcp's fail-open behavior so stale env vars don't
|
|
83
|
+
* break unrelated commands).
|
|
84
|
+
*/
|
|
85
|
+
export declare function loadProfile(name?: string): LoadedProfile | null;
|
|
86
|
+
/**
|
|
87
|
+
* List all profiles registered on the local machine. Each entry includes
|
|
88
|
+
* the profile name + its metadata. Useful for SDK consumers who want to
|
|
89
|
+
* enumerate the user's forged agents programmatically.
|
|
90
|
+
*
|
|
91
|
+
* Returns an empty array if ~/.nookplot/profiles doesn't exist. Never
|
|
92
|
+
* throws — invalid profiles are silently skipped.
|
|
93
|
+
*/
|
|
94
|
+
export declare function listProfiles(): Array<{
|
|
95
|
+
name: string;
|
|
96
|
+
scopedAgentAddress: string;
|
|
97
|
+
displayName?: string;
|
|
98
|
+
hermesProfile?: string;
|
|
99
|
+
}>;
|
|
100
|
+
//# sourceMappingURL=loadProfile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loadProfile.d.ts","sourceRoot":"","sources":["../src/loadProfile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkDG;AAMH,MAAM,WAAW,aAAa;IAC5B,iFAAiF;IACjF,MAAM,EAAE,MAAM,CAAC;IACf,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,UAAU,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,UAAU,EAAE,MAAM,CAAC;IACnB,kFAAkF;IAClF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,4EAA4E;IAC5E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,iEAAiE;IACjE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAsBD;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAa7E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,IAAI,CAAC,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAwE/D;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,IAAI,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,kBAAkB,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA8BhI"}
|