@greenlandai/sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +208 -0
- package/README.md +54 -0
- package/dist/index.cjs +1011 -0
- package/index.js +40 -0
- package/package.json +33 -0
- package/src/core/crypto.js +301 -0
- package/src/core/errors.js +33 -0
- package/src/core/idempotency.js +83 -0
- package/src/core/transport.js +124 -0
- package/src/enyal.js +165 -0
- package/src/gldai.js +40 -0
- package/src/joulepai.js +116 -0
- package/src/rareeai.js +66 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1011 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// index.js
|
|
20
|
+
var js_exports = {};
|
|
21
|
+
__export(js_exports, {
|
|
22
|
+
AuthError: () => AuthError,
|
|
23
|
+
Client: () => Client,
|
|
24
|
+
ConflictError: () => ConflictError,
|
|
25
|
+
ENFORCED: () => ENFORCED,
|
|
26
|
+
GreenlandAIError: () => GreenlandAIError,
|
|
27
|
+
IDEMPOTENCY_MAP: () => IDEMPOTENCY_MAP,
|
|
28
|
+
NOT_SUPPORTED: () => NOT_SUPPORTED,
|
|
29
|
+
NetworkError: () => NetworkError,
|
|
30
|
+
NotFoundError: () => NotFoundError,
|
|
31
|
+
RateLimitError: () => RateLimitError,
|
|
32
|
+
SAFE: () => SAFE,
|
|
33
|
+
ScopeError: () => ScopeError,
|
|
34
|
+
ServerError: () => ServerError,
|
|
35
|
+
VERSION: () => VERSION,
|
|
36
|
+
ValidationError: () => ValidationError,
|
|
37
|
+
crypto: () => crypto_exports
|
|
38
|
+
});
|
|
39
|
+
module.exports = __toCommonJS(js_exports);
|
|
40
|
+
|
|
41
|
+
// src/core/errors.js
|
|
42
|
+
var GreenlandAIError = class extends Error {
|
|
43
|
+
constructor(statusCode, detail, requestId = null, retryAfter = null) {
|
|
44
|
+
super(statusCode ? `${statusCode}: ${detail}` : String(detail));
|
|
45
|
+
this.name = this.constructor.name;
|
|
46
|
+
this.statusCode = statusCode;
|
|
47
|
+
this.detail = detail;
|
|
48
|
+
this.requestId = requestId;
|
|
49
|
+
this.retryAfter = retryAfter;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
var AuthError = class extends GreenlandAIError {
|
|
53
|
+
};
|
|
54
|
+
var ScopeError = class extends GreenlandAIError {
|
|
55
|
+
};
|
|
56
|
+
var NotFoundError = class extends GreenlandAIError {
|
|
57
|
+
};
|
|
58
|
+
var ValidationError = class extends GreenlandAIError {
|
|
59
|
+
};
|
|
60
|
+
var ConflictError = class extends GreenlandAIError {
|
|
61
|
+
};
|
|
62
|
+
var RateLimitError = class extends GreenlandAIError {
|
|
63
|
+
};
|
|
64
|
+
var ServerError = class extends GreenlandAIError {
|
|
65
|
+
};
|
|
66
|
+
var NetworkError = class extends GreenlandAIError {
|
|
67
|
+
constructor(detail) {
|
|
68
|
+
super(null, detail);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
function errorForStatus(statusCode, detail, requestId = null, retryAfter = null) {
|
|
72
|
+
if (statusCode === 401) return new AuthError(statusCode, detail, requestId);
|
|
73
|
+
if (statusCode === 403) return new ScopeError(statusCode, detail, requestId);
|
|
74
|
+
if (statusCode === 404) return new NotFoundError(statusCode, detail, requestId);
|
|
75
|
+
if (statusCode === 400 || statusCode === 422) return new ValidationError(statusCode, detail, requestId);
|
|
76
|
+
if (statusCode === 409) return new ConflictError(statusCode, detail, requestId, retryAfter);
|
|
77
|
+
if (statusCode === 429) return new RateLimitError(statusCode, detail, requestId, retryAfter);
|
|
78
|
+
if (statusCode >= 500) return new ServerError(statusCode, detail, requestId);
|
|
79
|
+
return new GreenlandAIError(statusCode, detail, requestId);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// src/core/idempotency.js
|
|
83
|
+
var import_node_crypto = require("node:crypto");
|
|
84
|
+
var ENFORCED = "ENFORCED";
|
|
85
|
+
var NOT_SUPPORTED = "NOT_SUPPORTED";
|
|
86
|
+
var SAFE = "SAFE";
|
|
87
|
+
var IDEMPOTENCY_MAP = {
|
|
88
|
+
// Class A — Pattern A body-PK
|
|
89
|
+
"POST /api/v1/archive": { field: "client_chunk_id", placement: "body", tier: ENFORCED },
|
|
90
|
+
"POST /api/v1/timestamp": { field: "client_chunk_id", placement: "body", tier: ENFORCED },
|
|
91
|
+
"POST /api/v1/agreement/create": { field: "client_chunk_id", placement: "body", tier: ENFORCED },
|
|
92
|
+
"POST /api/v1/compliance/attest": { field: "client_attestation_id", placement: "body", tier: ENFORCED },
|
|
93
|
+
// Class B — Pattern B body idempotency_key
|
|
94
|
+
"POST /api/v1/prove": { field: "idempotency_key", placement: "body", tier: ENFORCED },
|
|
95
|
+
"POST /api/v1/prove-batch": { field: "idempotency_key", placement: "body", tier: ENFORCED },
|
|
96
|
+
"POST /api/v1/prove/share-combination": { field: "idempotency_key", placement: "body", tier: ENFORCED },
|
|
97
|
+
"POST /api/v1/disclose": { field: "idempotency_key", placement: "body", tier: ENFORCED },
|
|
98
|
+
"POST /api/v1/disclose/client-side": { field: "idempotency_key", placement: "body", tier: ENFORCED },
|
|
99
|
+
"POST /api/v1/message/send": { field: "idempotency_key", placement: "body", tier: ENFORCED },
|
|
100
|
+
"POST /api/v1/provide": { field: "idempotency_key", placement: "body", tier: ENFORCED },
|
|
101
|
+
"POST /api/v1/request": { field: "idempotency_key", placement: "body", tier: ENFORCED },
|
|
102
|
+
"POST /api/v1/deliver/{match_id}": { field: "idempotency_key", placement: "body", tier: ENFORCED },
|
|
103
|
+
"POST /api/v1/match/{match_id}/resolve": { field: "idempotency_key", placement: "body", tier: ENFORCED },
|
|
104
|
+
"POST /api/v1/marketplace/provide": { field: "idempotency_key", placement: "body", tier: ENFORCED },
|
|
105
|
+
"POST /api/v1/marketplace/request": { field: "idempotency_key", placement: "body", tier: ENFORCED },
|
|
106
|
+
"POST /api/v1/marketplace/deliver/{match_id}": { field: "idempotency_key", placement: "body", tier: ENFORCED },
|
|
107
|
+
"POST /api/v1/marketplace/match/{match_id}/resolve": { field: "idempotency_key", placement: "body", tier: ENFORCED },
|
|
108
|
+
// Class C — header key (metered gldai)
|
|
109
|
+
"GET /api/v1/companies": { field: "X-Idempotency-Key", placement: "header", tier: ENFORCED },
|
|
110
|
+
"GET /api/v1/deposits": { field: "X-Idempotency-Key", placement: "header", tier: ENFORCED },
|
|
111
|
+
"GET /api/v1/infrastructure": { field: "X-Idempotency-Key", placement: "header", tier: ENFORCED },
|
|
112
|
+
"GET /api/v1/query": { field: "X-Idempotency-Key", placement: "header", tier: ENFORCED },
|
|
113
|
+
"GET /api/v1/projects": { field: "X-Idempotency-Key", placement: "header", tier: ENFORCED },
|
|
114
|
+
"GET /api/v1/mapdata": { field: "X-Idempotency-Key", placement: "header", tier: ENFORCED },
|
|
115
|
+
// Class D — transfer-class
|
|
116
|
+
"POST /api/v1/wallet/transfer": { field: "idempotency_key", placement: "body", tier: ENFORCED },
|
|
117
|
+
// G1 demotions / unenforced mutating endpoints — explicit NOT_SUPPORTED
|
|
118
|
+
"POST /api/v1/wallet/fund": { field: null, placement: null, tier: NOT_SUPPORTED },
|
|
119
|
+
"POST /api/v1/match/{match_id}/approve": { field: null, placement: null, tier: NOT_SUPPORTED },
|
|
120
|
+
"POST /api/v1/match/{match_id}/reject": { field: null, placement: null, tier: NOT_SUPPORTED },
|
|
121
|
+
"POST /api/v1/match/{match_id}/dispute": { field: null, placement: null, tier: NOT_SUPPORTED },
|
|
122
|
+
"POST /api/v1/match/{match_id}/extend-dispute": { field: null, placement: null, tier: NOT_SUPPORTED },
|
|
123
|
+
"POST /api/v1/oracle/confirm/{match_id}": { field: null, placement: null, tier: NOT_SUPPORTED },
|
|
124
|
+
"POST /api/v1/oracle/register": { field: null, placement: null, tier: NOT_SUPPORTED },
|
|
125
|
+
"POST /api/v1/marketplace/match/{match_id}/approve": { field: null, placement: null, tier: NOT_SUPPORTED },
|
|
126
|
+
"POST /api/v1/marketplace/match/{match_id}/reject": { field: null, placement: null, tier: NOT_SUPPORTED },
|
|
127
|
+
"POST /api/v1/marketplace/match/{match_id}/dispute": { field: null, placement: null, tier: NOT_SUPPORTED },
|
|
128
|
+
"POST /api/v1/marketplace/match/{match_id}/extend-dispute": { field: null, placement: null, tier: NOT_SUPPORTED },
|
|
129
|
+
"POST /api/v1/knowledge/upgrade": { field: null, placement: null, tier: NOT_SUPPORTED },
|
|
130
|
+
"POST /api/v1/memory/structured-query": { field: null, placement: null, tier: NOT_SUPPORTED },
|
|
131
|
+
"POST /api/v1/memory/content-query": { field: null, placement: null, tier: NOT_SUPPORTED },
|
|
132
|
+
"POST /api/v1/proof/queue": { field: null, placement: null, tier: NOT_SUPPORTED }
|
|
133
|
+
};
|
|
134
|
+
function lookup(method, pathTemplate) {
|
|
135
|
+
return IDEMPOTENCY_MAP[`${method.toUpperCase()} ${pathTemplate}`] ?? null;
|
|
136
|
+
}
|
|
137
|
+
function dispatch(method, pathTemplate, body, headers, idempotencyKey) {
|
|
138
|
+
const entry = lookup(method, pathTemplate);
|
|
139
|
+
if (!entry) {
|
|
140
|
+
return { body, headers, tier: method.toUpperCase() === "GET" ? SAFE : ENFORCED, resolvedKey: null };
|
|
141
|
+
}
|
|
142
|
+
if (entry.tier === NOT_SUPPORTED || !entry.field) {
|
|
143
|
+
return { body, headers, tier: entry.tier, resolvedKey: null };
|
|
144
|
+
}
|
|
145
|
+
const resolved = idempotencyKey || (0, import_node_crypto.randomUUID)();
|
|
146
|
+
if (entry.placement === "body") {
|
|
147
|
+
body = { ...body || {}, [entry.field]: resolved };
|
|
148
|
+
} else if (entry.placement === "header") {
|
|
149
|
+
headers = { ...headers, [entry.field]: resolved };
|
|
150
|
+
}
|
|
151
|
+
return { body, headers, tier: entry.tier, resolvedKey: resolved };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// src/core/transport.js
|
|
155
|
+
var DEFAULT_MAX_RETRIES = 3;
|
|
156
|
+
var DEFAULT_INITIAL_DELAY = 500;
|
|
157
|
+
var DEFAULT_MAX_DELAY = 8e3;
|
|
158
|
+
var DEFAULT_BACKOFF_FACTOR = 2;
|
|
159
|
+
var DEFAULT_JITTER_MIN = 100;
|
|
160
|
+
var DEFAULT_JITTER_MAX = 300;
|
|
161
|
+
var sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
162
|
+
function isRetryable(err) {
|
|
163
|
+
return err instanceof NetworkError || err instanceof RateLimitError || err instanceof ServerError;
|
|
164
|
+
}
|
|
165
|
+
var noopLogger = { debug() {
|
|
166
|
+
}, info() {
|
|
167
|
+
}, warn() {
|
|
168
|
+
}, error() {
|
|
169
|
+
} };
|
|
170
|
+
var Transport = class {
|
|
171
|
+
constructor({
|
|
172
|
+
apiKey,
|
|
173
|
+
oauthToken,
|
|
174
|
+
logger,
|
|
175
|
+
onRetry,
|
|
176
|
+
onTerminalFailure,
|
|
177
|
+
onAuthExpired,
|
|
178
|
+
metrics,
|
|
179
|
+
fetchImpl
|
|
180
|
+
} = {}) {
|
|
181
|
+
if (!apiKey && !oauthToken) throw new Error("apiKey or oauthToken required");
|
|
182
|
+
this._credential = apiKey || oauthToken;
|
|
183
|
+
this.logger = logger || noopLogger;
|
|
184
|
+
this.onRetry = onRetry || null;
|
|
185
|
+
this.onTerminalFailure = onTerminalFailure || null;
|
|
186
|
+
this.onAuthExpired = onAuthExpired || null;
|
|
187
|
+
this.metrics = metrics || null;
|
|
188
|
+
this._fetch = fetchImpl || fetch;
|
|
189
|
+
}
|
|
190
|
+
async request(baseUrl, method, pathTemplate, {
|
|
191
|
+
pathParams = {},
|
|
192
|
+
body = null,
|
|
193
|
+
params = null,
|
|
194
|
+
idempotencyKey = null,
|
|
195
|
+
retry = true,
|
|
196
|
+
maxRetries = DEFAULT_MAX_RETRIES
|
|
197
|
+
} = {}) {
|
|
198
|
+
let headers = { Authorization: `Bearer ${this._credential}` };
|
|
199
|
+
const d = dispatch(method, pathTemplate, body, headers, idempotencyKey);
|
|
200
|
+
body = d.body;
|
|
201
|
+
headers = d.headers;
|
|
202
|
+
if (d.tier === NOT_SUPPORTED) retry = false;
|
|
203
|
+
let path = pathTemplate;
|
|
204
|
+
for (const [k, v] of Object.entries(pathParams)) path = path.replace(`{${k}}`, encodeURIComponent(v));
|
|
205
|
+
let url = `${baseUrl.replace(/\/+$/, "")}${path}`;
|
|
206
|
+
if (params) {
|
|
207
|
+
const qs = new URLSearchParams(
|
|
208
|
+
Object.fromEntries(Object.entries(params).filter(([, v]) => v != null))
|
|
209
|
+
);
|
|
210
|
+
url += `?${qs}`;
|
|
211
|
+
}
|
|
212
|
+
let attempt = 0;
|
|
213
|
+
const t0 = Date.now();
|
|
214
|
+
for (; ; ) {
|
|
215
|
+
try {
|
|
216
|
+
const result = await this._once(method, url, headers, body);
|
|
217
|
+
this._count("sdk.requests");
|
|
218
|
+
this._hist("sdk.request.duration", (Date.now() - t0) / 1e3);
|
|
219
|
+
if (result && result.duplicate === true) {
|
|
220
|
+
this._count("sdk.duplicates");
|
|
221
|
+
this.logger.info(`duplicate response (idempotent replay) ${method} ${path}`);
|
|
222
|
+
}
|
|
223
|
+
return result;
|
|
224
|
+
} catch (err) {
|
|
225
|
+
if (err.statusCode === 401 && this.onAuthExpired) this.onAuthExpired(err);
|
|
226
|
+
if (!retry || attempt >= maxRetries || !isRetryable(err)) {
|
|
227
|
+
if (this.onTerminalFailure) this.onTerminalFailure(err, attempt + 1);
|
|
228
|
+
this.logger.warn(`terminal failure ${method} ${path} after ${attempt + 1} attempt(s)`);
|
|
229
|
+
throw err;
|
|
230
|
+
}
|
|
231
|
+
let delay = Math.min(DEFAULT_INITIAL_DELAY * DEFAULT_BACKOFF_FACTOR ** attempt, DEFAULT_MAX_DELAY) + DEFAULT_JITTER_MIN + Math.random() * (DEFAULT_JITTER_MAX - DEFAULT_JITTER_MIN);
|
|
232
|
+
if (err.retryAfter) delay = Math.max(delay, Number(err.retryAfter) * 1e3);
|
|
233
|
+
attempt += 1;
|
|
234
|
+
this._count("sdk.retries");
|
|
235
|
+
if (this.onRetry) this.onRetry(attempt, delay, err);
|
|
236
|
+
this.logger.info(`retry ${attempt}/${maxRetries} ${method} ${path} in ${Math.round(delay)}ms`);
|
|
237
|
+
await sleep(delay);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
async _once(method, url, headers, body) {
|
|
242
|
+
const redacted = { ...headers, Authorization: "[REDACTED]" };
|
|
243
|
+
this.logger.debug(`request ${method} ${url} headers=${JSON.stringify(redacted)}`);
|
|
244
|
+
let resp;
|
|
245
|
+
try {
|
|
246
|
+
const opts = { method, headers: { ...headers } };
|
|
247
|
+
if (body != null) {
|
|
248
|
+
opts.headers["Content-Type"] = "application/json";
|
|
249
|
+
opts.body = JSON.stringify(body);
|
|
250
|
+
}
|
|
251
|
+
resp = await this._fetch(url, opts);
|
|
252
|
+
} catch (err) {
|
|
253
|
+
throw new NetworkError(String(err && err.message ? err.message : err));
|
|
254
|
+
}
|
|
255
|
+
const requestId = resp.headers?.get ? resp.headers.get("x-request-id") : null;
|
|
256
|
+
const ra = resp.headers?.get ? resp.headers.get("retry-after") : null;
|
|
257
|
+
const retryAfter = ra ? Number(ra) : null;
|
|
258
|
+
if (resp.status >= 400) {
|
|
259
|
+
let detail;
|
|
260
|
+
try {
|
|
261
|
+
const data = await resp.json();
|
|
262
|
+
detail = data.detail ?? resp.statusText;
|
|
263
|
+
} catch {
|
|
264
|
+
detail = resp.statusText;
|
|
265
|
+
}
|
|
266
|
+
if (typeof detail === "object") detail = JSON.stringify(detail);
|
|
267
|
+
throw errorForStatus(resp.status, String(detail), requestId, retryAfter);
|
|
268
|
+
}
|
|
269
|
+
if (resp.status === 204) return null;
|
|
270
|
+
return resp.json();
|
|
271
|
+
}
|
|
272
|
+
_count(name) {
|
|
273
|
+
if (this.metrics) this.metrics.count(name, 1);
|
|
274
|
+
}
|
|
275
|
+
_hist(name, v) {
|
|
276
|
+
if (this.metrics) this.metrics.histogram(name, v);
|
|
277
|
+
}
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
// src/core/crypto.js
|
|
281
|
+
var crypto_exports = {};
|
|
282
|
+
__export(crypto_exports, {
|
|
283
|
+
combineSharesAndDecrypt: () => combineSharesAndDecrypt,
|
|
284
|
+
decryptKnowledgeNode: () => decryptKnowledgeNode,
|
|
285
|
+
deriveSharesFromMnemonic: () => deriveSharesFromMnemonic,
|
|
286
|
+
verifyShareCombination: () => verifyShareCombination
|
|
287
|
+
});
|
|
288
|
+
var import_bip39 = require("@scure/bip39");
|
|
289
|
+
var import_english = require("@scure/bip39/wordlists/english");
|
|
290
|
+
var GF256_EXP = new Uint8Array(512);
|
|
291
|
+
var GF256_LOG = new Uint8Array(256);
|
|
292
|
+
(function initGF256() {
|
|
293
|
+
let x = 1;
|
|
294
|
+
for (let i = 0; i < 255; i++) {
|
|
295
|
+
GF256_EXP[i] = x;
|
|
296
|
+
GF256_LOG[x] = i;
|
|
297
|
+
let hi = x << 1;
|
|
298
|
+
if (hi & 256) hi ^= 283;
|
|
299
|
+
x = hi ^ x;
|
|
300
|
+
}
|
|
301
|
+
for (let i = 255; i < 512; i++) {
|
|
302
|
+
GF256_EXP[i] = GF256_EXP[i - 255];
|
|
303
|
+
}
|
|
304
|
+
})();
|
|
305
|
+
function gf256Mul(a, b) {
|
|
306
|
+
if (a === 0 || b === 0) return 0;
|
|
307
|
+
return GF256_EXP[(GF256_LOG[a] + GF256_LOG[b]) % 255];
|
|
308
|
+
}
|
|
309
|
+
function gf256Inv(a) {
|
|
310
|
+
if (a === 0) throw new Error("Zero has no inverse in GF(256)");
|
|
311
|
+
return GF256_EXP[255 - GF256_LOG[a]];
|
|
312
|
+
}
|
|
313
|
+
function shamirCombine(share1, share2) {
|
|
314
|
+
if (share1.length !== 33 || share2.length !== 33) {
|
|
315
|
+
throw new Error("Share combination failed \u2014 each share must be 33 bytes");
|
|
316
|
+
}
|
|
317
|
+
const x1 = share1[0], x2 = share2[0];
|
|
318
|
+
if (x1 === 0 || x2 === 0 || x1 === x2) {
|
|
319
|
+
throw new Error("Share combination failed \u2014 invalid share indices");
|
|
320
|
+
}
|
|
321
|
+
const d = x1 ^ x2;
|
|
322
|
+
const dInv = gf256Inv(d);
|
|
323
|
+
const secret = new Uint8Array(32);
|
|
324
|
+
for (let i = 0; i < 32; i++) {
|
|
325
|
+
const y1 = share1[1 + i], y2 = share2[1 + i];
|
|
326
|
+
const num = gf256Mul(y1, x2) ^ gf256Mul(y2, x1);
|
|
327
|
+
secret[i] = gf256Mul(num, dInv);
|
|
328
|
+
}
|
|
329
|
+
return secret;
|
|
330
|
+
}
|
|
331
|
+
function hexToBytes(hex) {
|
|
332
|
+
hex = hex.trim();
|
|
333
|
+
if (hex.length % 2 !== 0) throw new Error("Hex string has odd length");
|
|
334
|
+
const bytes = new Uint8Array(hex.length / 2);
|
|
335
|
+
for (let i = 0; i < hex.length; i += 2) {
|
|
336
|
+
bytes[i / 2] = parseInt(hex.substr(i, 2), 16);
|
|
337
|
+
}
|
|
338
|
+
return bytes;
|
|
339
|
+
}
|
|
340
|
+
function bytesToHex(bytes) {
|
|
341
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
342
|
+
}
|
|
343
|
+
function base64ToBytes(b64) {
|
|
344
|
+
const bin = atob(b64);
|
|
345
|
+
const bytes = new Uint8Array(bin.length);
|
|
346
|
+
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
|
|
347
|
+
return bytes;
|
|
348
|
+
}
|
|
349
|
+
async function memoryKDF(sharedSecret) {
|
|
350
|
+
const salt = new Uint8Array(32);
|
|
351
|
+
const prkKey = await crypto.subtle.importKey("raw", salt, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
352
|
+
const prk = new Uint8Array(await crypto.subtle.sign("HMAC", prkKey, sharedSecret));
|
|
353
|
+
const okmKey = await crypto.subtle.importKey("raw", prk, { name: "HMAC", hash: "SHA-256" }, false, ["sign"]);
|
|
354
|
+
const context = new TextEncoder().encode("joulepai-memory-v1");
|
|
355
|
+
const info = new Uint8Array(context.length + 1);
|
|
356
|
+
info.set(context);
|
|
357
|
+
info[context.length] = 1;
|
|
358
|
+
return new Uint8Array(await crypto.subtle.sign("HMAC", okmKey, info));
|
|
359
|
+
}
|
|
360
|
+
async function aesGcmDecrypt(keyBytes, iv, ciphertext, tag) {
|
|
361
|
+
const aesKey = await crypto.subtle.importKey("raw", keyBytes, { name: "AES-GCM" }, false, ["decrypt"]);
|
|
362
|
+
const ctWithTag = new Uint8Array(ciphertext.length + tag.length);
|
|
363
|
+
ctWithTag.set(ciphertext);
|
|
364
|
+
ctWithTag.set(tag, ciphertext.length);
|
|
365
|
+
try {
|
|
366
|
+
const plaintext = await crypto.subtle.decrypt(
|
|
367
|
+
{ name: "AES-GCM", iv, tagLength: 128 },
|
|
368
|
+
aesKey,
|
|
369
|
+
ctWithTag
|
|
370
|
+
);
|
|
371
|
+
return new Uint8Array(plaintext);
|
|
372
|
+
} catch (_) {
|
|
373
|
+
throw new Error(
|
|
374
|
+
"Share combination failed \u2014 invalid recovery phrase or share. Please verify your recovery phrase and try again."
|
|
375
|
+
);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
async function combineSharesAndDecrypt(customerShare, custodialShare, chunk, p256ScalarMul) {
|
|
379
|
+
const privateKey = shamirCombine(customerShare, custodialShare);
|
|
380
|
+
const ephemPub = hexToBytes(chunk.encryption_metadata.ecdh_public_key_hex);
|
|
381
|
+
const sharedSecretX = await p256ScalarMul(privateKey, ephemPub);
|
|
382
|
+
const aesKey = await memoryKDF(sharedSecretX);
|
|
383
|
+
const iv = hexToBytes(chunk.encryption_metadata.iv_hex);
|
|
384
|
+
const tag = hexToBytes(chunk.encryption_metadata.tag_hex);
|
|
385
|
+
const ct = base64ToBytes(chunk.encrypted_payload);
|
|
386
|
+
return aesGcmDecrypt(aesKey, iv, ct, tag);
|
|
387
|
+
}
|
|
388
|
+
async function verifyShareCombination(customerShare, custodialShare, poseidonKeyHash, wasmUrl) {
|
|
389
|
+
const moduleUrl = wasmUrl || "/static/shamir_verify.js";
|
|
390
|
+
const mod = await import(moduleUrl);
|
|
391
|
+
await mod.default(moduleUrl.replace(".js", ".wasm"));
|
|
392
|
+
const result = mod.verify_share_combination(
|
|
393
|
+
bytesToHex(customerShare),
|
|
394
|
+
bytesToHex(custodialShare),
|
|
395
|
+
poseidonKeyHash
|
|
396
|
+
);
|
|
397
|
+
return JSON.parse(result);
|
|
398
|
+
}
|
|
399
|
+
function deriveSharesFromMnemonic(mnemonicPhrase, passphrase = "") {
|
|
400
|
+
const norm = mnemonicPhrase.trim().split(/\s+/).join(" ");
|
|
401
|
+
if (!(0, import_bip39.validateMnemonic)(norm, import_english.wordlist)) {
|
|
402
|
+
throw new ValidationError(null, "Invalid BIP39 mnemonic (bad word or checksum)");
|
|
403
|
+
}
|
|
404
|
+
const seed = (0, import_bip39.mnemonicToSeedSync)(norm, passphrase);
|
|
405
|
+
const customerShare = new Uint8Array(33);
|
|
406
|
+
customerShare[0] = 1;
|
|
407
|
+
customerShare.set(seed.slice(0, 32), 1);
|
|
408
|
+
return { seed, customerShare };
|
|
409
|
+
}
|
|
410
|
+
async function decryptKnowledgeNode(node, keyBase64) {
|
|
411
|
+
const key = await crypto.subtle.importKey(
|
|
412
|
+
"raw",
|
|
413
|
+
Uint8Array.from(atob(keyBase64), (c) => c.charCodeAt(0)),
|
|
414
|
+
{ name: "AES-GCM" },
|
|
415
|
+
false,
|
|
416
|
+
["decrypt"]
|
|
417
|
+
);
|
|
418
|
+
node.name = await _decryptNodeField(node.name, key);
|
|
419
|
+
node.summary = await _decryptNodeField(node.summary, key);
|
|
420
|
+
return node;
|
|
421
|
+
}
|
|
422
|
+
async function _decryptNodeField(b64, key) {
|
|
423
|
+
if (!b64) return "";
|
|
424
|
+
try {
|
|
425
|
+
const d = Uint8Array.from(atob(b64), (c) => c.charCodeAt(0));
|
|
426
|
+
if (d.length < 13) return b64;
|
|
427
|
+
const dec = await crypto.subtle.decrypt({ name: "AES-GCM", iv: d.slice(0, 12) }, key, d.slice(12));
|
|
428
|
+
return new TextDecoder().decode(dec);
|
|
429
|
+
} catch {
|
|
430
|
+
return b64;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// src/enyal.js
|
|
435
|
+
var ENYAL_BASE = "https://api.enyal.ai";
|
|
436
|
+
var EnyalSubmodule = class {
|
|
437
|
+
constructor(transport, baseUrl = ENYAL_BASE) {
|
|
438
|
+
this._t = transport;
|
|
439
|
+
this._base = baseUrl;
|
|
440
|
+
this.crypto = crypto_exports;
|
|
441
|
+
}
|
|
442
|
+
// ── class A (ENFORCED) ──
|
|
443
|
+
/** 2.1.0: enyal-client.js:444. */
|
|
444
|
+
archive({ agentId, chunkType, chunkKey, data, metadata, proofTier, idempotencyKey, retry = true }) {
|
|
445
|
+
if (typeof data !== "string") data = JSON.stringify(data);
|
|
446
|
+
const body = {
|
|
447
|
+
agent_id: agentId,
|
|
448
|
+
chunk_type: chunkType,
|
|
449
|
+
chunk_key: chunkKey,
|
|
450
|
+
data: Buffer.from(data).toString("base64")
|
|
451
|
+
};
|
|
452
|
+
if (metadata) body.metadata = metadata;
|
|
453
|
+
if (proofTier) body.proof_tier = proofTier;
|
|
454
|
+
return this._t.request(this._base, "POST", "/api/v1/archive", { body, idempotencyKey, retry });
|
|
455
|
+
}
|
|
456
|
+
/** 2.1.0: enyal-client.js:499. ENFORCED (G1: trust_routes.py:123-138). */
|
|
457
|
+
timestamp({ payload, description, idempotencyKey, retry = true }) {
|
|
458
|
+
if (typeof payload !== "string") payload = JSON.stringify(payload);
|
|
459
|
+
const body = { payload };
|
|
460
|
+
if (description) body.description = description;
|
|
461
|
+
return this._t.request(this._base, "POST", "/api/v1/timestamp", { body, idempotencyKey, retry });
|
|
462
|
+
}
|
|
463
|
+
/** ENFORCED (G1: trust_routes.py:223-238). */
|
|
464
|
+
createAgreement({ terms, parties, title, idempotencyKey, retry = true }) {
|
|
465
|
+
const body = { terms, parties };
|
|
466
|
+
if (title) body.title = title;
|
|
467
|
+
return this._t.request(this._base, "POST", "/api/v1/agreement/create", { body, idempotencyKey, retry });
|
|
468
|
+
}
|
|
469
|
+
/** ENFORCED (G1: trust_routes.py:453-471). */
|
|
470
|
+
complianceAttest({ periodStart, periodEnd, systems, idempotencyKey, retry = true }) {
|
|
471
|
+
return this._t.request(
|
|
472
|
+
this._base,
|
|
473
|
+
"POST",
|
|
474
|
+
"/api/v1/compliance/attest",
|
|
475
|
+
{ body: { period_start: periodStart, period_end: periodEnd, systems }, idempotencyKey, retry }
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
// ── class B (ENFORCED) ──
|
|
479
|
+
/** 2.1.0: enyal-client.js:472. ENFORCED (prove_routes.py:204). */
|
|
480
|
+
prove({
|
|
481
|
+
resourceType,
|
|
482
|
+
geographicRegion,
|
|
483
|
+
supplyChainStage,
|
|
484
|
+
chunkIds,
|
|
485
|
+
quantumResistant = false,
|
|
486
|
+
idempotencyKey,
|
|
487
|
+
retry = true
|
|
488
|
+
} = {}) {
|
|
489
|
+
const body = { quantum_resistant: quantumResistant };
|
|
490
|
+
if (resourceType) body.resource_type = resourceType;
|
|
491
|
+
if (geographicRegion) body.geographic_region = geographicRegion;
|
|
492
|
+
if (supplyChainStage) body.supply_chain_stage = supplyChainStage;
|
|
493
|
+
if (chunkIds) body.chunk_ids = chunkIds;
|
|
494
|
+
return this._t.request(this._base, "POST", "/api/v1/prove", { body, idempotencyKey, retry });
|
|
495
|
+
}
|
|
496
|
+
proveBatch({ queries, quantumResistant = true, compress = true, idempotencyKey, retry = true }) {
|
|
497
|
+
return this._t.request(
|
|
498
|
+
this._base,
|
|
499
|
+
"POST",
|
|
500
|
+
"/api/v1/prove-batch",
|
|
501
|
+
{ body: { queries, quantum_resistant: quantumResistant, compress }, idempotencyKey, retry }
|
|
502
|
+
);
|
|
503
|
+
}
|
|
504
|
+
/** 2.1.0: enyal-client.js:292. ENFORCED (prove_routes.py:666). */
|
|
505
|
+
proveShareCombination({ customerShareHex, poseidonKeyHash, idempotencyKey, retry = true }) {
|
|
506
|
+
const body = { customer_share_hex: customerShareHex };
|
|
507
|
+
if (poseidonKeyHash) body.poseidon_key_hash = poseidonKeyHash;
|
|
508
|
+
return this._t.request(this._base, "POST", "/api/v1/prove/share-combination", { body, idempotencyKey, retry });
|
|
509
|
+
}
|
|
510
|
+
/** 2.1.0: enyal-client.js:483. ENFORCED (disclose_routes.py:274). */
|
|
511
|
+
disclose({ chunkIds, recipientPubkeyHex, purpose, idempotencyKey, retry = true }) {
|
|
512
|
+
return this._t.request(
|
|
513
|
+
this._base,
|
|
514
|
+
"POST",
|
|
515
|
+
"/api/v1/disclose",
|
|
516
|
+
{ body: { chunk_ids: chunkIds, recipient_pubkey_hex: recipientPubkeyHex, purpose }, idempotencyKey, retry }
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
/** 2.1.0: enyal-client.js:201. ENFORCED (disclose_routes.py:654). */
|
|
520
|
+
requestClientDisclosure({ chunkIds, purpose, idempotencyKey, retry = true }) {
|
|
521
|
+
return this._t.request(
|
|
522
|
+
this._base,
|
|
523
|
+
"POST",
|
|
524
|
+
"/api/v1/disclose/client-side",
|
|
525
|
+
{ body: { chunk_ids: chunkIds, purpose }, idempotencyKey, retry }
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
/** ENFORCED (message_routes.py:312). */
|
|
529
|
+
sendMessage({ senderAgentId, threadId, recipientAgentId, messageType, payload, idempotencyKey, retry = true }) {
|
|
530
|
+
return this._t.request(
|
|
531
|
+
this._base,
|
|
532
|
+
"POST",
|
|
533
|
+
"/api/v1/message/send",
|
|
534
|
+
{
|
|
535
|
+
body: {
|
|
536
|
+
sender_agent_id: senderAgentId,
|
|
537
|
+
thread_id: threadId,
|
|
538
|
+
recipient_agent_id: recipientAgentId,
|
|
539
|
+
message_type: messageType,
|
|
540
|
+
payload
|
|
541
|
+
},
|
|
542
|
+
idempotencyKey,
|
|
543
|
+
retry
|
|
544
|
+
}
|
|
545
|
+
);
|
|
546
|
+
}
|
|
547
|
+
getInbox({ agentId, direction = "inbox", ...params } = {}) {
|
|
548
|
+
return this._t.request(
|
|
549
|
+
this._base,
|
|
550
|
+
"GET",
|
|
551
|
+
"/api/v1/message/inbox",
|
|
552
|
+
{ params: { agent_id: agentId, direction, ...params } }
|
|
553
|
+
);
|
|
554
|
+
}
|
|
555
|
+
getThread(threadId) {
|
|
556
|
+
return this._t.request(
|
|
557
|
+
this._base,
|
|
558
|
+
"GET",
|
|
559
|
+
"/api/v1/message/thread/{thread_id}",
|
|
560
|
+
{ pathParams: { thread_id: threadId } }
|
|
561
|
+
);
|
|
562
|
+
}
|
|
563
|
+
markRead(messageIds, retry = true) {
|
|
564
|
+
return this._t.request(
|
|
565
|
+
this._base,
|
|
566
|
+
"POST",
|
|
567
|
+
"/api/v1/message/read",
|
|
568
|
+
{ body: { message_ids: messageIds }, retry }
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
// ── reads ──
|
|
572
|
+
search(params = {}) {
|
|
573
|
+
return this._t.request(this._base, "GET", "/api/v1/search", { params });
|
|
574
|
+
}
|
|
575
|
+
verifyAgreement({ agreementChunkId, terms }) {
|
|
576
|
+
return this._t.request(
|
|
577
|
+
this._base,
|
|
578
|
+
"GET",
|
|
579
|
+
"/api/v1/agreement/verify",
|
|
580
|
+
{ params: { agreement_chunk_id: agreementChunkId, terms } }
|
|
581
|
+
);
|
|
582
|
+
}
|
|
583
|
+
getLineage(chunkId) {
|
|
584
|
+
return this._t.request(this._base, "GET", "/api/v1/lineage/{chunk_id}", { pathParams: { chunk_id: chunkId } });
|
|
585
|
+
}
|
|
586
|
+
getKnowledgeNodes(params = {}) {
|
|
587
|
+
return this._t.request(this._base, "GET", "/api/v1/knowledge/nodes", { params });
|
|
588
|
+
}
|
|
589
|
+
getKnowledgeNode(nodeId) {
|
|
590
|
+
return this._t.request(this._base, "GET", "/api/v1/knowledge/node/{node_id}", { pathParams: { node_id: nodeId } });
|
|
591
|
+
}
|
|
592
|
+
getKnowledgeConnections(nodeId, hops = 2) {
|
|
593
|
+
return this._t.request(
|
|
594
|
+
this._base,
|
|
595
|
+
"GET",
|
|
596
|
+
"/api/v1/knowledge/connections/{node_id}",
|
|
597
|
+
{ pathParams: { node_id: nodeId }, params: { hops } }
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
getContradictions() {
|
|
601
|
+
return this._t.request(this._base, "GET", "/api/v1/knowledge/contradictions", {});
|
|
602
|
+
}
|
|
603
|
+
getKnowledgeStats() {
|
|
604
|
+
return this._t.request(this._base, "GET", "/api/v1/knowledge/stats", {});
|
|
605
|
+
}
|
|
606
|
+
getKnowledgeIndex() {
|
|
607
|
+
return this._t.request(this._base, "GET", "/api/v1/knowledge/index", {});
|
|
608
|
+
}
|
|
609
|
+
getKnowledgeHealth() {
|
|
610
|
+
return this._t.request(this._base, "GET", "/api/v1/knowledge/health", {});
|
|
611
|
+
}
|
|
612
|
+
/** Absorbed stray (AUDIT.6: index.js:342). */
|
|
613
|
+
getContext({ depth = 1, topic, format = "json" } = {}) {
|
|
614
|
+
const params = { depth, format };
|
|
615
|
+
if (topic) params.topic = topic;
|
|
616
|
+
return this._t.request(this._base, "GET", "/api/v1/knowledge/context", { params });
|
|
617
|
+
}
|
|
618
|
+
/** Session-auth only — raise-stub preserved per 2.1.0 + design §6. */
|
|
619
|
+
synthesiseKnowledge() {
|
|
620
|
+
throw new Error("knowledge/synthesise requires session auth (not API key). Use the ENYAL web console at enyal.ai.");
|
|
621
|
+
}
|
|
622
|
+
// ── NOT SUPPORTED tier (auto-retry OFF at wrapper) ──
|
|
623
|
+
/** NOT retry-safe (knowledge_routes.py:448 — 500j billed, no server idempotency). */
|
|
624
|
+
knowledgeUpgrade(body = {}) {
|
|
625
|
+
return this._t.request(this._base, "POST", "/api/v1/knowledge/upgrade", { body });
|
|
626
|
+
}
|
|
627
|
+
/** NOT retry-safe (knowledge_routes.py:1337). */
|
|
628
|
+
memoryStructuredQuery(body = {}) {
|
|
629
|
+
return this._t.request(this._base, "POST", "/api/v1/memory/structured-query", { body });
|
|
630
|
+
}
|
|
631
|
+
/** NOT retry-safe (knowledge_routes.py:1374). */
|
|
632
|
+
memoryContentQuery(body = {}) {
|
|
633
|
+
return this._t.request(this._base, "POST", "/api/v1/memory/content-query", { body });
|
|
634
|
+
}
|
|
635
|
+
/** NOT retry-safe (archive_routes.py:440). */
|
|
636
|
+
queueProof(chunkId, quantumResistant = false) {
|
|
637
|
+
return this._t.request(
|
|
638
|
+
this._base,
|
|
639
|
+
"POST",
|
|
640
|
+
"/api/v1/proof/queue",
|
|
641
|
+
{ body: { chunk_id: chunkId, quantum_resistant: quantumResistant } }
|
|
642
|
+
);
|
|
643
|
+
}
|
|
644
|
+
// ── recovery round-trip (design §4.1.3; wrapped-master-key invariant) ──
|
|
645
|
+
async recoverFromRecoveryFile(recoveryFile, mnemonic, passphrase = "") {
|
|
646
|
+
if (typeof recoveryFile === "string") recoveryFile = JSON.parse(recoveryFile);
|
|
647
|
+
const { customerShare } = deriveSharesFromMnemonic(mnemonic, passphrase);
|
|
648
|
+
const custodial = (void 0)(recoveryFile.custodial_share_hex);
|
|
649
|
+
return combineSharesAndDecrypt(customerShare, custodial, recoveryFile.encrypted_payload);
|
|
650
|
+
}
|
|
651
|
+
// ── Arch-3 stubs (design §4.1.4 — activate when render phases B-D ship) ──
|
|
652
|
+
memoryQuery() {
|
|
653
|
+
throw new Error("NotImplemented: Render phases B-D not yet deployed (Arch-3, REV15-7)");
|
|
654
|
+
}
|
|
655
|
+
executePlan() {
|
|
656
|
+
throw new Error("NotImplemented: Render phases B-D not yet deployed (Arch-3, REV15-7)");
|
|
657
|
+
}
|
|
658
|
+
renderNode() {
|
|
659
|
+
throw new Error("NotImplemented: Render phases B-D not yet deployed (Arch-3, REV15-7)");
|
|
660
|
+
}
|
|
661
|
+
renderContext() {
|
|
662
|
+
throw new Error("NotImplemented: Render phases B-D not yet deployed (Arch-3, REV15-7)");
|
|
663
|
+
}
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
// src/joulepai.js
|
|
667
|
+
var JOULEPAI_BASE = "https://joulepai.ai/api/v1";
|
|
668
|
+
var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
669
|
+
var JoulepaiSubmodule = class {
|
|
670
|
+
constructor(transport, baseUrl = JOULEPAI_BASE) {
|
|
671
|
+
this._t = transport;
|
|
672
|
+
this._base = baseUrl;
|
|
673
|
+
this._walletId = null;
|
|
674
|
+
}
|
|
675
|
+
async _ensureId() {
|
|
676
|
+
if (!this._walletId) {
|
|
677
|
+
this._walletId = (await this._t.request(this._base, "GET", "/wallet/me", {})).id;
|
|
678
|
+
}
|
|
679
|
+
return this._walletId;
|
|
680
|
+
}
|
|
681
|
+
/** Absorbs Wallet.create (index.js:50). */
|
|
682
|
+
createWallet({ email, ownerId, ownerType = "human", handle }) {
|
|
683
|
+
const body = { owner_id: ownerId || crypto.randomUUID(), owner_type: ownerType, email };
|
|
684
|
+
if (handle) body.handle = handle;
|
|
685
|
+
return this._t.request(this._base, "POST", "/wallet/create", { body });
|
|
686
|
+
}
|
|
687
|
+
/** Absorbs pay() (index.js:117). ENFORCED class D — dispatch auto-generates the key. */
|
|
688
|
+
async transfer(destination, amount, {
|
|
689
|
+
platform = "rareeai",
|
|
690
|
+
note,
|
|
691
|
+
privacyMode,
|
|
692
|
+
idempotencyKey,
|
|
693
|
+
retry = true
|
|
694
|
+
} = {}) {
|
|
695
|
+
const body = { from_wallet_id: await this._ensureId(), amount, platform };
|
|
696
|
+
const dest = destination.replace(/^@/, "");
|
|
697
|
+
body[UUID_RE.test(dest) ? "to_wallet_id" : "to_handle"] = dest;
|
|
698
|
+
if (note != null) body.note = note;
|
|
699
|
+
if (privacyMode != null) body.privacy_mode = privacyMode;
|
|
700
|
+
return this._t.request(this._base, "POST", "/api/v1/wallet/transfer", { body, idempotencyKey, retry });
|
|
701
|
+
}
|
|
702
|
+
pay(...args) {
|
|
703
|
+
return this.transfer(...args);
|
|
704
|
+
}
|
|
705
|
+
transferStatus(transferId) {
|
|
706
|
+
return this._t.request(this._base, "GET", "/wallet/transfer/{tid}/status", { pathParams: { tid: transferId } });
|
|
707
|
+
}
|
|
708
|
+
/** Absorbs fund() (index.js:152). NOT retry-safe (G1 demotion — duplicate Stripe sessions). */
|
|
709
|
+
async fund(amountFiat, { method = "stripe" } = {}) {
|
|
710
|
+
return this._t.request(
|
|
711
|
+
this._base,
|
|
712
|
+
"POST",
|
|
713
|
+
"/api/v1/wallet/fund",
|
|
714
|
+
{ body: { wallet_id: await this._ensureId(), amount_fiat: amountFiat, method } }
|
|
715
|
+
);
|
|
716
|
+
}
|
|
717
|
+
async balance() {
|
|
718
|
+
return (await this._t.request(
|
|
719
|
+
this._base,
|
|
720
|
+
"GET",
|
|
721
|
+
"/wallet/balance/{wid}",
|
|
722
|
+
{ pathParams: { wid: await this._ensureId() } }
|
|
723
|
+
)).balance;
|
|
724
|
+
}
|
|
725
|
+
async info() {
|
|
726
|
+
return this._t.request(
|
|
727
|
+
this._base,
|
|
728
|
+
"GET",
|
|
729
|
+
"/wallet/balance/{wid}",
|
|
730
|
+
{ pathParams: { wid: await this._ensureId() } }
|
|
731
|
+
);
|
|
732
|
+
}
|
|
733
|
+
async handle() {
|
|
734
|
+
return (await this.info()).handle ?? null;
|
|
735
|
+
}
|
|
736
|
+
async transactions({ limit = 50, offset = 0 } = {}) {
|
|
737
|
+
return this._t.request(
|
|
738
|
+
this._base,
|
|
739
|
+
"GET",
|
|
740
|
+
"/wallet/transactions/{wid}",
|
|
741
|
+
{ pathParams: { wid: await this._ensureId() }, params: { limit, offset } }
|
|
742
|
+
);
|
|
743
|
+
}
|
|
744
|
+
async setHandle(h) {
|
|
745
|
+
return this._t.request(
|
|
746
|
+
this._base,
|
|
747
|
+
"PUT",
|
|
748
|
+
"/wallet/handle",
|
|
749
|
+
{ body: { wallet_id: await this._ensureId(), handle: h.replace(/^@/, "") } }
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
async exchangeRate(currency = "EUR") {
|
|
753
|
+
return (await this._t.request(
|
|
754
|
+
this._base,
|
|
755
|
+
"GET",
|
|
756
|
+
"/wallet/exchange-rate/{cur}",
|
|
757
|
+
{ pathParams: { cur: currency.toUpperCase() } }
|
|
758
|
+
)).joules_per_unit;
|
|
759
|
+
}
|
|
760
|
+
async getLimits() {
|
|
761
|
+
return this._t.request(
|
|
762
|
+
this._base,
|
|
763
|
+
"GET",
|
|
764
|
+
"/wallet/limits/{wid}",
|
|
765
|
+
{ pathParams: { wid: await this._ensureId() } }
|
|
766
|
+
);
|
|
767
|
+
}
|
|
768
|
+
async setLimits({ daily, perTransaction, allowedPlatforms } = {}) {
|
|
769
|
+
const body = { agent_wallet_id: await this._ensureId() };
|
|
770
|
+
if (daily != null) body.daily_cap = daily;
|
|
771
|
+
if (perTransaction != null) body.per_transaction_cap = perTransaction;
|
|
772
|
+
if (allowedPlatforms != null) body.allowed_platforms = allowedPlatforms;
|
|
773
|
+
return this._t.request(this._base, "PUT", "/wallet/limits", { body });
|
|
774
|
+
}
|
|
775
|
+
async depositInfo() {
|
|
776
|
+
return this._t.request(
|
|
777
|
+
this._base,
|
|
778
|
+
"GET",
|
|
779
|
+
"/wallet/deposit-info/{wid}",
|
|
780
|
+
{ pathParams: { wid: await this._ensureId() } }
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
depositAddress() {
|
|
784
|
+
return this._t.request(this._base, "GET", "/wallet/deposit-address", {});
|
|
785
|
+
}
|
|
786
|
+
fundingStatus(fundingEventId) {
|
|
787
|
+
return this._t.request(this._base, "GET", "/wallet/fund/{fid}", { pathParams: { fid: fundingEventId } });
|
|
788
|
+
}
|
|
789
|
+
resetCredentials(walletId, email) {
|
|
790
|
+
return this._t.request(
|
|
791
|
+
this._base,
|
|
792
|
+
"POST",
|
|
793
|
+
"/wallet/reset-credentials",
|
|
794
|
+
{ body: { wallet_id: walletId, email } }
|
|
795
|
+
);
|
|
796
|
+
}
|
|
797
|
+
verify(transactionId) {
|
|
798
|
+
return this._t.request(this._base, "GET", "/wallet/transaction/{tid}", { pathParams: { tid: transactionId } });
|
|
799
|
+
}
|
|
800
|
+
async verifyPayment(transactionId, expectedAmount, recipient) {
|
|
801
|
+
return (await this._t.request(
|
|
802
|
+
this._base,
|
|
803
|
+
"POST",
|
|
804
|
+
"/wallet/verify-payment",
|
|
805
|
+
{ params: { transaction_id: transactionId, expected_amount: expectedAmount, recipient } }
|
|
806
|
+
)).verified;
|
|
807
|
+
}
|
|
808
|
+
};
|
|
809
|
+
|
|
810
|
+
// src/rareeai.js
|
|
811
|
+
var DIRECT_BASE = "https://joulepai.ai";
|
|
812
|
+
var UNIFIED_BASE = "https://joulepai.ai";
|
|
813
|
+
var DIRECT_PREFIX = "/api/v1";
|
|
814
|
+
var UNIFIED_PREFIX = "/api/v1/marketplace";
|
|
815
|
+
var RareeaiEntry = class {
|
|
816
|
+
constructor(transport, baseUrl, prefix, rateLabel) {
|
|
817
|
+
this._t = transport;
|
|
818
|
+
this._base = baseUrl;
|
|
819
|
+
this._p = prefix;
|
|
820
|
+
this.rate = rateLabel;
|
|
821
|
+
}
|
|
822
|
+
// Pattern B — ENFORCED (G1 row 5)
|
|
823
|
+
createProvider({ resourceType, capacity, idempotencyKey, retry = true, ...fields }) {
|
|
824
|
+
const body = { resource_type: resourceType, ...fields };
|
|
825
|
+
if (capacity) body.capacity = capacity;
|
|
826
|
+
return this._t.request(this._base, "POST", `${this._p}/provide`, { body, idempotencyKey, retry });
|
|
827
|
+
}
|
|
828
|
+
createRequest({ resourceType, idempotencyKey, retry = true, ...fields }) {
|
|
829
|
+
return this._t.request(
|
|
830
|
+
this._base,
|
|
831
|
+
"POST",
|
|
832
|
+
`${this._p}/request`,
|
|
833
|
+
{ body: { resource_type: resourceType, ...fields }, idempotencyKey, retry }
|
|
834
|
+
);
|
|
835
|
+
}
|
|
836
|
+
deliver(matchId, { idempotencyKey, retry = true, ...fields } = {}) {
|
|
837
|
+
return this._t.request(
|
|
838
|
+
this._base,
|
|
839
|
+
"POST",
|
|
840
|
+
`${this._p}/deliver/{match_id}`,
|
|
841
|
+
{ pathParams: { match_id: matchId }, body: fields, idempotencyKey, retry }
|
|
842
|
+
);
|
|
843
|
+
}
|
|
844
|
+
resolve(matchId, { idempotencyKey, retry = true, ...fields } = {}) {
|
|
845
|
+
return this._t.request(
|
|
846
|
+
this._base,
|
|
847
|
+
"POST",
|
|
848
|
+
`${this._p}/match/{match_id}/resolve`,
|
|
849
|
+
{ pathParams: { match_id: matchId }, body: fields, idempotencyKey, retry }
|
|
850
|
+
);
|
|
851
|
+
}
|
|
852
|
+
// FSM transitions — NOT SUPPORTED (auto-retry OFF; G1 row 5)
|
|
853
|
+
approve(matchId, fields = {}) {
|
|
854
|
+
return this._t.request(
|
|
855
|
+
this._base,
|
|
856
|
+
"POST",
|
|
857
|
+
`${this._p}/match/{match_id}/approve`,
|
|
858
|
+
{ pathParams: { match_id: matchId }, body: fields }
|
|
859
|
+
);
|
|
860
|
+
}
|
|
861
|
+
reject(matchId, fields = {}) {
|
|
862
|
+
return this._t.request(
|
|
863
|
+
this._base,
|
|
864
|
+
"POST",
|
|
865
|
+
`${this._p}/match/{match_id}/reject`,
|
|
866
|
+
{ pathParams: { match_id: matchId }, body: fields }
|
|
867
|
+
);
|
|
868
|
+
}
|
|
869
|
+
dispute(matchId, fields = {}) {
|
|
870
|
+
return this._t.request(
|
|
871
|
+
this._base,
|
|
872
|
+
"POST",
|
|
873
|
+
`${this._p}/match/{match_id}/dispute`,
|
|
874
|
+
{ pathParams: { match_id: matchId }, body: fields }
|
|
875
|
+
);
|
|
876
|
+
}
|
|
877
|
+
extendDispute(matchId, fields = {}) {
|
|
878
|
+
return this._t.request(
|
|
879
|
+
this._base,
|
|
880
|
+
"POST",
|
|
881
|
+
`${this._p}/match/{match_id}/extend-dispute`,
|
|
882
|
+
{ pathParams: { match_id: matchId }, body: fields }
|
|
883
|
+
);
|
|
884
|
+
}
|
|
885
|
+
// reads
|
|
886
|
+
providers(params = {}) {
|
|
887
|
+
return this._t.request(this._base, "GET", `${this._p}/providers`, { params });
|
|
888
|
+
}
|
|
889
|
+
myProviders() {
|
|
890
|
+
return this._t.request(this._base, "GET", `${this._p}/providers/mine`, {});
|
|
891
|
+
}
|
|
892
|
+
getRequest(requestId) {
|
|
893
|
+
return this._t.request(
|
|
894
|
+
this._base,
|
|
895
|
+
"GET",
|
|
896
|
+
`${this._p}/request/{request_id}`,
|
|
897
|
+
{ pathParams: { request_id: requestId } }
|
|
898
|
+
);
|
|
899
|
+
}
|
|
900
|
+
};
|
|
901
|
+
var RareeaiSubmodule = class {
|
|
902
|
+
constructor(transport, directBase = DIRECT_BASE, unifiedBase = UNIFIED_BASE) {
|
|
903
|
+
this.direct = new RareeaiEntry(transport, directBase, DIRECT_PREFIX, "0.25%");
|
|
904
|
+
this.intelligence = new RareeaiEntry(transport, unifiedBase, UNIFIED_PREFIX, "0.5%");
|
|
905
|
+
}
|
|
906
|
+
};
|
|
907
|
+
|
|
908
|
+
// src/gldai.js
|
|
909
|
+
var GLDAI_BASE = "https://greenlandai.ai";
|
|
910
|
+
var GldaiSubmodule = class {
|
|
911
|
+
constructor(transport, baseUrl = GLDAI_BASE) {
|
|
912
|
+
this._t = transport;
|
|
913
|
+
this._base = baseUrl;
|
|
914
|
+
}
|
|
915
|
+
companies({ idempotencyKey, ...params } = {}) {
|
|
916
|
+
return this._t.request(this._base, "GET", "/api/v1/companies", { params, idempotencyKey });
|
|
917
|
+
}
|
|
918
|
+
company(companyId) {
|
|
919
|
+
return this._t.request(this._base, "GET", "/api/v1/companies/{cid}", { pathParams: { cid: companyId } });
|
|
920
|
+
}
|
|
921
|
+
deposits({ idempotencyKey, ...params } = {}) {
|
|
922
|
+
return this._t.request(this._base, "GET", "/api/v1/deposits", { params, idempotencyKey });
|
|
923
|
+
}
|
|
924
|
+
deposit(depositId) {
|
|
925
|
+
return this._t.request(this._base, "GET", "/api/v1/deposits/{did}", { pathParams: { did: depositId } });
|
|
926
|
+
}
|
|
927
|
+
infrastructureList({ idempotencyKey, ...params } = {}) {
|
|
928
|
+
return this._t.request(this._base, "GET", "/api/v1/infrastructure", { params, idempotencyKey });
|
|
929
|
+
}
|
|
930
|
+
infrastructure(infraId) {
|
|
931
|
+
return this._t.request(this._base, "GET", "/api/v1/infrastructure/{iid}", { pathParams: { iid: infraId } });
|
|
932
|
+
}
|
|
933
|
+
query(q, { idempotencyKey, ...params } = {}) {
|
|
934
|
+
return this._t.request(this._base, "GET", "/api/v1/query", { params: { q, ...params }, idempotencyKey });
|
|
935
|
+
}
|
|
936
|
+
projects({ idempotencyKey, ...params } = {}) {
|
|
937
|
+
return this._t.request(this._base, "GET", "/api/v1/projects", { params, idempotencyKey });
|
|
938
|
+
}
|
|
939
|
+
project(projectId) {
|
|
940
|
+
return this._t.request(this._base, "GET", "/api/v1/projects/{pid}", { pathParams: { pid: projectId } });
|
|
941
|
+
}
|
|
942
|
+
mapdata({ idempotencyKey, ...params } = {}) {
|
|
943
|
+
return this._t.request(this._base, "GET", "/api/v1/mapdata", { params, idempotencyKey });
|
|
944
|
+
}
|
|
945
|
+
};
|
|
946
|
+
|
|
947
|
+
// index.js
|
|
948
|
+
var VERSION = "0.1.0";
|
|
949
|
+
var Client = class {
|
|
950
|
+
/**
|
|
951
|
+
* @param {object} opts
|
|
952
|
+
* @param {string} [opts.apiKey] - eyl_ API key (primary for SDK consumers).
|
|
953
|
+
* @param {string} [opts.oauthToken] - ENYAL-issued OAuth JWT (web sessions).
|
|
954
|
+
* @param {object} [opts.logger] - console-shaped logger (console/winston/pino).
|
|
955
|
+
* @param {function} [opts.onRetry] / [opts.onTerminalFailure] / [opts.onAuthExpired]
|
|
956
|
+
* @param {object} [opts.metrics] - {count(name,v), histogram(name,v)} sink (OFF default).
|
|
957
|
+
*/
|
|
958
|
+
constructor({
|
|
959
|
+
apiKey,
|
|
960
|
+
oauthToken,
|
|
961
|
+
logger,
|
|
962
|
+
onRetry,
|
|
963
|
+
onTerminalFailure,
|
|
964
|
+
onAuthExpired,
|
|
965
|
+
metrics,
|
|
966
|
+
fetchImpl,
|
|
967
|
+
enyalBaseUrl,
|
|
968
|
+
joulepaiBaseUrl,
|
|
969
|
+
rareeaiDirectBaseUrl,
|
|
970
|
+
rareeaiUnifiedBaseUrl,
|
|
971
|
+
gldaiBaseUrl
|
|
972
|
+
} = {}) {
|
|
973
|
+
this._transport = new Transport({
|
|
974
|
+
apiKey,
|
|
975
|
+
oauthToken,
|
|
976
|
+
logger,
|
|
977
|
+
onRetry,
|
|
978
|
+
onTerminalFailure,
|
|
979
|
+
onAuthExpired,
|
|
980
|
+
metrics,
|
|
981
|
+
fetchImpl
|
|
982
|
+
});
|
|
983
|
+
this.enyal = new EnyalSubmodule(this._transport, enyalBaseUrl || ENYAL_BASE);
|
|
984
|
+
this.joulepai = new JoulepaiSubmodule(this._transport, joulepaiBaseUrl || JOULEPAI_BASE);
|
|
985
|
+
this.rareeai = new RareeaiSubmodule(
|
|
986
|
+
this._transport,
|
|
987
|
+
rareeaiDirectBaseUrl || DIRECT_BASE,
|
|
988
|
+
rareeaiUnifiedBaseUrl || UNIFIED_BASE
|
|
989
|
+
);
|
|
990
|
+
this.gldai = new GldaiSubmodule(this._transport, gldaiBaseUrl || GLDAI_BASE);
|
|
991
|
+
}
|
|
992
|
+
};
|
|
993
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
994
|
+
0 && (module.exports = {
|
|
995
|
+
AuthError,
|
|
996
|
+
Client,
|
|
997
|
+
ConflictError,
|
|
998
|
+
ENFORCED,
|
|
999
|
+
GreenlandAIError,
|
|
1000
|
+
IDEMPOTENCY_MAP,
|
|
1001
|
+
NOT_SUPPORTED,
|
|
1002
|
+
NetworkError,
|
|
1003
|
+
NotFoundError,
|
|
1004
|
+
RateLimitError,
|
|
1005
|
+
SAFE,
|
|
1006
|
+
ScopeError,
|
|
1007
|
+
ServerError,
|
|
1008
|
+
VERSION,
|
|
1009
|
+
ValidationError,
|
|
1010
|
+
crypto
|
|
1011
|
+
});
|