@onesi/genesis 0.1.13 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/auto.d.ts +1010 -0
- package/dist/auto.js +125 -0
- package/dist/beacons.d.ts +42 -0
- package/dist/beacons.js +71 -0
- package/dist/blinds-owner-enrollment.d.ts +121 -0
- package/dist/blinds-owner-enrollment.js +23 -2
- package/dist/boxes.d.ts +1074 -0
- package/dist/boxes.js +338 -0
- package/dist/client.d.ts +3 -19947
- package/dist/client.js +127 -18
- package/dist/errors.d.ts +6 -0
- package/dist/errors.js +38 -3
- package/dist/flares.d.ts +32 -0
- package/dist/flares.js +5 -0
- package/dist/fleets.d.ts +32 -0
- package/dist/fleets.js +59 -6
- package/dist/flocks.d.ts +334 -1
- package/dist/flocks.js +85 -0
- package/dist/foundry.d.ts +429 -0
- package/dist/foundry.js +135 -0
- package/dist/garden.d.ts +754 -0
- package/dist/garden.js +76 -0
- package/dist/gateway.d.ts +365 -0
- package/dist/gateway.js +82 -0
- package/dist/gears.d.ts +121 -2
- package/dist/gears.js +24 -2
- package/dist/generated/operations.d.ts +3 -19948
- package/dist/generated/operations.js +64214 -13423
- package/dist/generated/schema.d.ts +85708 -17364
- package/dist/genesis.d.ts +9041 -1250
- package/dist/genesis.js +92 -25
- package/dist/genesis.openapi.json +133172 -31267
- package/dist/henry.d.ts +202 -1
- package/dist/henry.js +223 -0
- package/dist/hives-cells.d.ts +8 -3
- package/dist/hives-cells.js +17 -4
- package/dist/hives-environment.d.ts +9 -0
- package/dist/hives-environment.js +22 -0
- package/dist/hives-node.js +4 -1
- package/dist/hives-operations.d.ts +121 -3
- package/dist/hives-operations.js +2 -0
- package/dist/hives-platform.d.ts +149 -0
- package/dist/hives-platform.js +183 -0
- package/dist/hives-resources-transport.d.ts +7 -1
- package/dist/hives-resources-transport.js +10 -2
- package/dist/hives-sse.d.ts +24 -0
- package/dist/hives-sse.js +111 -0
- package/dist/hives-topics.d.ts +4 -3
- package/dist/hives-topics.js +9 -4
- package/dist/iam-directory.d.ts +8 -0
- package/dist/iam-directory.js +23 -0
- package/dist/iam.d.ts +397 -0
- package/dist/iam.js +92 -0
- package/dist/idempotency.d.ts +3 -0
- package/dist/idempotency.js +27 -0
- package/dist/index.d.ts +22 -6
- package/dist/index.js +19 -5
- package/dist/instances.d.ts +177 -2
- package/dist/instances.js +37 -5
- package/dist/minds-agent.d.ts +28 -0
- package/dist/minds-agent.js +53 -10
- package/dist/minds-engine.d.ts +394 -0
- package/dist/minds-engine.js +555 -0
- package/dist/minds.d.ts +109 -1
- package/dist/minds.js +49 -14
- package/dist/orgs.d.ts +405 -0
- package/dist/orgs.js +63 -0
- package/dist/redact.d.ts +37 -0
- package/dist/redact.js +330 -0
- package/dist/runner.d.ts +4 -0
- package/dist/runner.js +1 -0
- package/dist/scopes.d.ts +316 -0
- package/dist/scopes.js +40 -0
- package/dist/taps.d.ts +118 -0
- package/dist/taps.js +45 -0
- package/dist/traces.d.ts +574 -0
- package/dist/traces.js +97 -0
- package/dist/types.d.ts +12 -1
- package/package.json +1 -1
package/dist/client.js
CHANGED
|
@@ -9,12 +9,18 @@ import { isHivesObjectUpload, hivesObjectBody, hivesContentType, isHivesResource
|
|
|
9
9
|
import { operationTable, services } from './generated/operations.js';
|
|
10
10
|
import { GenesisError } from './errors.js';
|
|
11
11
|
import { requireSecureUrl } from './urls.js';
|
|
12
|
+
import { iamDirectoryFailure, isIamDirectoryOperation } from './iam-directory.js';
|
|
12
13
|
import { validateHivesInvocation, hivesFunctionResponseHeaders, hivesFunctionMetadata, hivesOperationError } from './hives-functions.js';
|
|
13
|
-
import { validateHivesEnvironment, hivesEnvironmentMetadata, hivesEnvironmentReveal } from './hives-environment.js';
|
|
14
|
+
import { validateHivesEnvironment, validateHivesEnvironmentNames, hivesEnvironmentSetNames, hivesEnvironmentMetadata, hivesEnvironmentReveal } from './hives-environment.js';
|
|
14
15
|
import { RUNNER_SELF_GRANT_OPERATION, snapshotRunnerSelfGrant, runnerGrantResponse, runnerGrantTransportError } from './runner-grant.js';
|
|
15
16
|
/** The Genesis API gateway. `https://console.life.one/api` remains an alias for the same gateway. */
|
|
16
17
|
export const DEFAULT_BASE_URL = 'https://os.one.ml/api';
|
|
17
18
|
const env = globalThis.process?.env ?? {};
|
|
19
|
+
/** Endless Server-Sent Events streams and the bounded reader for each; request() refuses these and any operation generated with `stream: true`. */
|
|
20
|
+
const ENDLESS_STREAMS = {
|
|
21
|
+
hives_stream_logs: 'genesis.hives.logs.stream() or genesis.hives.logs.window()',
|
|
22
|
+
hives_stream_deployment_logs: 'hivesSseWindow(await genesis.raw(...))',
|
|
23
|
+
};
|
|
18
24
|
export class GenesisClient {
|
|
19
25
|
baseUrl;
|
|
20
26
|
routing;
|
|
@@ -45,11 +51,19 @@ export class GenesisClient {
|
|
|
45
51
|
const v = input[k];
|
|
46
52
|
if (v === undefined || v === null || v === '')
|
|
47
53
|
throw new GenesisError({ status: 0, type: 'invalid_request', message: `Missing required path parameter "${k}" for ${operationId}`, operationId });
|
|
48
|
-
|
|
54
|
+
if (!op.tailParams?.includes(k))
|
|
55
|
+
return encodeURIComponent(String(v));
|
|
56
|
+
const segments = String(v).split('/');
|
|
57
|
+
if (segments.some(s => s === '' || s === '.' || s === '..' || s.includes('\\')))
|
|
58
|
+
throw new GenesisError({ status: 0, type: 'invalid_request', operationId,
|
|
59
|
+
message: `Path parameter "${k}" for ${operationId} must be "/"-separated segments with no empty, "." or ".." segment and no backslash` });
|
|
60
|
+
return segments.map(encodeURIComponent).join('/');
|
|
49
61
|
});
|
|
50
62
|
let base;
|
|
51
63
|
let p;
|
|
52
64
|
if (this.routing === 'direct') {
|
|
65
|
+
if (op.service === 'gateway')
|
|
66
|
+
throw new GenesisError({ status: 0, type: 'invalid_request', operationId, message: 'Gateway operations are answered by the Genesis gateway itself and have no direct upstream.' });
|
|
53
67
|
base = (this.opts.upstreams?.[op.service] ?? op.upstream.base).replace(/\/+$/, '');
|
|
54
68
|
p = fill(op.upstream.path);
|
|
55
69
|
}
|
|
@@ -72,9 +86,21 @@ export class GenesisClient {
|
|
|
72
86
|
}
|
|
73
87
|
/** Call any Genesis operation by id, fully typed from the merged spec. */
|
|
74
88
|
async request(operationId, input, init = {}) {
|
|
89
|
+
// An endless SSE body never completes, so buffering it would hang: refuse before any request is sent.
|
|
90
|
+
// Finite SSE reads (for example traces_streamRun) are not marked and still use request().
|
|
91
|
+
const endless = ENDLESS_STREAMS[operationId]
|
|
92
|
+
?? (operationTable[operationId]?.stream === true ? 'hivesSseWindow(await genesis.raw(...))' : undefined);
|
|
93
|
+
if (endless)
|
|
94
|
+
throw new GenesisError({ status: 0, type: 'invalid_request', operationId,
|
|
95
|
+
message: `${String(operationId)} is an endless Server-Sent Events stream. Read a bounded window with ${endless} instead of request().` });
|
|
75
96
|
const res = await this.raw(operationId, (input ?? {}), init);
|
|
76
97
|
if (operationTable[operationId].rawResponse)
|
|
77
98
|
return res;
|
|
99
|
+
// DELETE /env answers 200 with an empty body.
|
|
100
|
+
if (operationId === 'hives_delete_project_env' || operationId === 'hives_delete_project_env_key') {
|
|
101
|
+
void res.body?.cancel().catch(() => { });
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
78
104
|
if (operationId === 'hives_stop_deployment' && res.status !== 200) {
|
|
79
105
|
void res.body?.cancel().catch(() => { });
|
|
80
106
|
throw new GenesisError({ status: 0, type: 'invalid_response', operationId, message: 'Hives did not confirm this deployment stopped. Read its current state before explicitly reconciling stop.' });
|
|
@@ -87,7 +113,7 @@ export class GenesisClient {
|
|
|
87
113
|
value = ct.includes('json') ? await res.json() : await res.text();
|
|
88
114
|
}
|
|
89
115
|
catch (error) {
|
|
90
|
-
if (String(operationId).startsWith('hives_') &&
|
|
116
|
+
if (String(operationId).startsWith('hives_') && /_project_env(?:ironment)?$/.test(operationId))
|
|
91
117
|
throw new Error('Hives returned invalid environment data.');
|
|
92
118
|
if (String(operationId).startsWith('hives_') && /_functions?$/.test(operationId))
|
|
93
119
|
throw new Error('Hives returned invalid function metadata.');
|
|
@@ -111,6 +137,9 @@ export class GenesisClient {
|
|
|
111
137
|
if (operationId === 'hives_list_project_environment' || operationId === 'hives_update_project_environment') {
|
|
112
138
|
return hivesEnvironmentMetadata(value, input?.project_id, operationId === 'hives_update_project_environment');
|
|
113
139
|
}
|
|
140
|
+
if (operationId === 'hives_set_project_env') {
|
|
141
|
+
return hivesEnvironmentSetNames(value, input?.project_id, input?.body);
|
|
142
|
+
}
|
|
114
143
|
if (operationId === 'hives_reveal_project_environment') {
|
|
115
144
|
return hivesEnvironmentReveal(value, input?.key);
|
|
116
145
|
}
|
|
@@ -118,6 +147,17 @@ export class GenesisClient {
|
|
|
118
147
|
}
|
|
119
148
|
/** Raw Response. Completed Hives guest responses retain their actual status, including non-2xx. */
|
|
120
149
|
async raw(operationId, input = {}, init = {}) {
|
|
150
|
+
if (!isIamDirectoryOperation(operationId))
|
|
151
|
+
return this.dispatch(operationId, input, init);
|
|
152
|
+
// The gateway binds the organization context itself. A refusal is only explained here; the client never changes a session or its context.
|
|
153
|
+
try {
|
|
154
|
+
return await this.dispatch(operationId, input, init);
|
|
155
|
+
}
|
|
156
|
+
catch (e) {
|
|
157
|
+
throw iamDirectoryFailure(e, typeof input.org_id === 'string' ? input.org_id : '', operationId);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
async dispatch(operationId, input, init) {
|
|
121
161
|
if (isMindsOwnerOperation(operationId))
|
|
122
162
|
return mindsOwnerTransport({ operationId, input, headers: init.headers, routing: this.routing, url: this.url(operationId, input), credential: () => this.credential(), fetch: this.fetchImpl, signal: init.signal });
|
|
123
163
|
if (isMindsAgentOperation(operationId))
|
|
@@ -139,13 +179,17 @@ export class GenesisClient {
|
|
|
139
179
|
const flocksNative = ['flocks_exchangeJoinToken', 'flocks_activateNode', 'flocks_rotateNodeCredential'].includes(operationId);
|
|
140
180
|
if (flocksNative && this.routing === 'direct')
|
|
141
181
|
throw new GenesisError({ status: 0, type: 'invalid_request', message: 'Native Flocks credentials are brokered only through the Genesis gateway.', operationId });
|
|
142
|
-
if (operationId === 'hives_update_project_environment')
|
|
182
|
+
if (operationId === 'hives_update_project_environment' || operationId === 'hives_set_project_env')
|
|
143
183
|
input = { ...input, body: validateHivesEnvironment(input.body) };
|
|
184
|
+
if (operationId === 'hives_delete_project_env')
|
|
185
|
+
input = { ...input, body: validateHivesEnvironmentNames(input.body) };
|
|
186
|
+
if (operationId === 'hives_delete_project_env_key')
|
|
187
|
+
validateHivesEnvironmentNames([input.key]);
|
|
144
188
|
const objectBody = isHivesObjectUpload(operationId) ? await hivesObjectBody(input.body) : undefined;
|
|
145
189
|
const url = this.url(operationId, input);
|
|
146
190
|
const headers = {
|
|
147
191
|
accept: 'application/json',
|
|
148
|
-
'user-agent': this.opts.userAgent ?? '@onesi/genesis/0.1
|
|
192
|
+
'user-agent': this.opts.userAgent ?? '@onesi/genesis/0.2.1',
|
|
149
193
|
...this.opts.headers, ...init.headers,
|
|
150
194
|
};
|
|
151
195
|
if (op.service === 'blinds' && !['GET', 'HEAD', 'OPTIONS'].includes(op.method))
|
|
@@ -203,7 +247,7 @@ export class GenesisClient {
|
|
|
203
247
|
body = JSON.stringify(input.body);
|
|
204
248
|
}
|
|
205
249
|
}
|
|
206
|
-
const environmentOperation = op.service === 'hives' &&
|
|
250
|
+
const environmentOperation = op.service === 'hives' && /_project_env(?:ironment|_key)?$/.test(operationId);
|
|
207
251
|
const secretOperation = op.service === 'hives' && (/_secrets?$/.test(operationId) || environmentOperation);
|
|
208
252
|
const functionOperation = op.service === 'hives' && (op.rawResponse || /_function(?:s|_async)?$/.test(operationId));
|
|
209
253
|
let res;
|
|
@@ -224,30 +268,50 @@ export class GenesisClient {
|
|
|
224
268
|
}
|
|
225
269
|
if (runnerGrant)
|
|
226
270
|
return runnerGrantResponse(res, runnerGrant);
|
|
271
|
+
// A raw (binary) answer outside Hives, e.g. a Panes screenshot: a success is the bytes; a
|
|
272
|
+
// failure takes the ordinary error path below.
|
|
273
|
+
if (op.rawResponse && op.service !== 'hives' && res.ok)
|
|
274
|
+
return res;
|
|
227
275
|
if (op.rawResponse && /^exec_[a-f0-9]{32}$/.test(res.headers.get('x-execution-id') ?? '')) {
|
|
228
276
|
return new Response(res.body, { status: res.status, statusText: res.statusText, headers: hivesFunctionResponseHeaders(res.headers) });
|
|
229
277
|
}
|
|
230
278
|
if (op.rawResponse && op.service === 'hives' && res.headers.has('x-hives-operation-id')) {
|
|
279
|
+
// 5xx stays invalid_response below: its outcome is uncertain, so keep the key and reconcile.
|
|
280
|
+
const copy = res.status >= 400 && res.status < 500 ? res.clone() : undefined;
|
|
231
281
|
const error = await hivesOperationError(res);
|
|
232
|
-
if (error)
|
|
282
|
+
if (error) {
|
|
283
|
+
void copy?.body?.cancel().catch(() => { });
|
|
233
284
|
throw error;
|
|
285
|
+
}
|
|
286
|
+
// A refusal envelope that is not an operation receipt keeps its own status, code and request id (finding G2).
|
|
287
|
+
const detail = copy ? await hivesErrorDetail(copy) : {};
|
|
288
|
+
if (detail.code !== undefined) {
|
|
289
|
+
throw new GenesisError({ status: res.status, type: `http_${res.status}`, operationId, code: detail.code,
|
|
290
|
+
message: detail.message ?? `Hives refused this invocation (${res.status}). Keep the original idempotency key and read its operation status before retrying.`,
|
|
291
|
+
requestId: res.headers.get('x-request-id') ?? detail.requestId });
|
|
292
|
+
}
|
|
234
293
|
throw new GenesisError({ status: res.status, type: 'invalid_response', operationId,
|
|
235
294
|
message: 'Hives operation status was not confirmed. Keep the original idempotency key and reconcile its operation.' });
|
|
236
295
|
}
|
|
237
|
-
if (op.rawResponse && res.ok)
|
|
296
|
+
if (op.rawResponse && op.service === 'hives' && res.ok)
|
|
238
297
|
throw new GenesisError({ status: res.status, type: 'invalid_response', operationId,
|
|
239
298
|
message: 'Hives did not confirm a completed guest execution. Keep the original invocation key and reconcile its operation.' });
|
|
240
299
|
if (functionOperation && !res.ok) {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
300
|
+
// Keep the service's own refusal code and text (findings G3, G4); the fixed texts are fallbacks only.
|
|
301
|
+
const detail = await hivesErrorDetail(res);
|
|
302
|
+
const invocation = op.rawResponse === true;
|
|
303
|
+
const scope = detail.code === undefined || /scope/i.test(detail.code);
|
|
304
|
+
const message = detail.code !== undefined && detail.message !== undefined && !(res.status === 403 && scope) ? detail.message
|
|
305
|
+
: res.status === 401 ? 'Genesis credential rejected. Sign in again.'
|
|
306
|
+
: res.status === 403 ? (scope ? 'Missing the authorized Hives scope for this function action.' : `Hives refused this ${invocation ? 'invocation' : 'function action'} (${detail.code}).`)
|
|
307
|
+
: res.status === 404 ? 'Function/deployment, project or artifact not found in the authenticated organization and project.'
|
|
308
|
+
: res.status === 409 ? (invocation ? `Hives refused this invocation as a conflict${detail.code ? ` (${detail.code})` : ''}. Read the target's current state before retrying.` : 'Function mutation conflicted. Refresh its metadata before retrying.')
|
|
309
|
+
: res.status === 400 || res.status === 422 ? 'Invalid function request. Check artifact, resource bounds and guest request syntax.'
|
|
310
|
+
: res.status === 413 ? 'Function request exceeds a body or serialized guest envelope limit.'
|
|
311
|
+
: res.status >= 300 && res.status < 400 ? 'Unexpected function service redirect. No redirect was followed.'
|
|
312
|
+
: `Hives function operation failed (${res.status}). Refresh and retry.`;
|
|
313
|
+
throw new GenesisError({ status: res.status, type: `http_${res.status}`, operationId, message, code: detail.code,
|
|
314
|
+
requestId: res.headers.get('x-request-id') ?? detail.requestId });
|
|
251
315
|
}
|
|
252
316
|
if (!res.ok && secretOperation) {
|
|
253
317
|
const message = res.status === 401 ? 'Genesis credential rejected. Sign in again.'
|
|
@@ -275,3 +339,48 @@ export class GenesisClient {
|
|
|
275
339
|
return res;
|
|
276
340
|
}
|
|
277
341
|
}
|
|
342
|
+
/**
|
|
343
|
+
* The refusal code, short message and request id of a Hives function/invocation error body. Reads at most
|
|
344
|
+
* 8 KiB and keeps nothing else: a body without a platform code may reflect guest request data.
|
|
345
|
+
*/
|
|
346
|
+
async function hivesErrorDetail(res) {
|
|
347
|
+
if (!res.body)
|
|
348
|
+
return {};
|
|
349
|
+
const reader = res.body.getReader();
|
|
350
|
+
const chunks = [];
|
|
351
|
+
let size = 0;
|
|
352
|
+
try {
|
|
353
|
+
for (;;) {
|
|
354
|
+
const { value, done } = await reader.read();
|
|
355
|
+
if (done)
|
|
356
|
+
break;
|
|
357
|
+
size += value.length;
|
|
358
|
+
if (size > 8192) {
|
|
359
|
+
await reader.cancel().catch(() => { });
|
|
360
|
+
return {};
|
|
361
|
+
}
|
|
362
|
+
chunks.push(value);
|
|
363
|
+
}
|
|
364
|
+
const bytes = new Uint8Array(size);
|
|
365
|
+
let offset = 0;
|
|
366
|
+
for (const chunk of chunks) {
|
|
367
|
+
bytes.set(chunk, offset);
|
|
368
|
+
offset += chunk.length;
|
|
369
|
+
}
|
|
370
|
+
const body = JSON.parse(new TextDecoder().decode(bytes));
|
|
371
|
+
const e = body && typeof body === 'object' ? body.error : undefined;
|
|
372
|
+
// Only an error envelope: a top-level `code` belongs to an operation receipt, never to a refusal.
|
|
373
|
+
const raw = e && typeof e === 'object' ? e.code : typeof e === 'string' ? e : undefined;
|
|
374
|
+
const code = typeof raw === 'string' && /^[A-Za-z][A-Za-z0-9_.:-]{0,63}$/.test(raw) ? raw : undefined;
|
|
375
|
+
const text = e && typeof e === 'object' ? e.message : undefined;
|
|
376
|
+
const message = code && typeof text === 'string' && text.length <= 300 && !/[\u0000-\u001f\u007f]/.test(text) ? text : undefined;
|
|
377
|
+
const id = (e && typeof e === 'object' ? e.request_id : undefined) ?? body?.request_id;
|
|
378
|
+
return { code, message, requestId: typeof id === 'string' && /^[A-Za-z0-9_.:-]{1,128}$/.test(id) ? id : undefined };
|
|
379
|
+
}
|
|
380
|
+
catch {
|
|
381
|
+
return {};
|
|
382
|
+
}
|
|
383
|
+
finally {
|
|
384
|
+
reader.releaseLock();
|
|
385
|
+
}
|
|
386
|
+
}
|
package/dist/errors.d.ts
CHANGED
|
@@ -33,3 +33,9 @@ export declare function errorOutcome(body: unknown): string | undefined;
|
|
|
33
33
|
/** Optional fields the gateway adds to an envelope; each must be a bounded string when present. */
|
|
34
34
|
export declare const ENVELOPE_EXTRA_FIELDS: readonly string[];
|
|
35
35
|
export declare const envelopeExtrasValid: (error: Record<string, unknown>) => boolean;
|
|
36
|
+
/**
|
|
37
|
+
* The service's error body as the CLI (`--json`) and MCP print it beside the envelope, redacted and bounded
|
|
38
|
+
* by `redactErrorBody` (see redact.ts). Undefined when the SDK kept no body: it deliberately drops bodies
|
|
39
|
+
* that may echo submitted secrets or guest data (Hives secret and function refusals).
|
|
40
|
+
*/
|
|
41
|
+
export declare function errorBody(e: GenesisError): unknown;
|
package/dist/errors.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { authorizationCode, redactErrorBody } from './redact.js';
|
|
1
2
|
/** Normalized error for every Genesis service (gateway `{error:{type,message}}`, IAM `{error,error_description}`, Relays `{error}`, Foundry/OpenAI shape). */
|
|
2
3
|
export class GenesisError extends Error {
|
|
3
4
|
status;
|
|
@@ -25,15 +26,41 @@ export class GenesisError extends Error {
|
|
|
25
26
|
// An OAuth-style token (`{"error":"forbidden"}`) is the type; any other string is a message, and type stays a stable token.
|
|
26
27
|
const type = typeof e === 'object' && e ? (e.type ?? e.code ?? 'error')
|
|
27
28
|
: typeof e === 'string' && /^[a-z][a-z0-9_]{0,63}$/.test(e) ? e : statusType(res.status);
|
|
29
|
+
const code = backendCode(body);
|
|
28
30
|
const message = (typeof e === 'object' && e?.message) || body?.error_description || body?.message ||
|
|
29
|
-
(typeof e === 'string' && e) || (typeof body === 'string' && body) || `${res.status} ${res.statusText}`;
|
|
31
|
+
(typeof e === 'string' && e) || (typeof body === 'string' && body) || `${res.status} ${res.statusText}${code ? ` (${code})` : ''}`;
|
|
30
32
|
return new GenesisError({
|
|
31
|
-
status: res.status, type, message, code
|
|
32
|
-
requestId: res.headers.get('x-request-id')
|
|
33
|
+
status: res.status, type, message, code,
|
|
34
|
+
requestId: [typeof e === 'object' ? e?.request_id : undefined, body?.request_id, res.headers.get('x-request-id')].find((v) => typeof v === 'string' && v !== ''),
|
|
33
35
|
operationId, body,
|
|
34
36
|
});
|
|
35
37
|
}
|
|
36
38
|
}
|
|
39
|
+
const CODE_TOKEN = /^[A-Za-z][A-Za-z0-9_.:-]{0,63}$/;
|
|
40
|
+
/** A refusal code is one case (`release_not_proven_safe`, `HARNESS_NOT_ON_NODE`), never a mixed-case random string. */
|
|
41
|
+
const ONE_CASE = /^(?:[a-z][a-z0-9_.:-]*|[A-Z][A-Z0-9_.:-]*)$/;
|
|
42
|
+
/**
|
|
43
|
+
* The refusal code of an error body. The envelope's `error.code` wins, unless it is only the gateway's generic
|
|
44
|
+
* `UPSTREAM_<status>`; otherwise the backend's own top-level `code`, which the gateway keeps beside its envelope
|
|
45
|
+
* or passes through unwrapped (Hives `{"code":"release_not_proven_safe",...}` on a 409 release).
|
|
46
|
+
*
|
|
47
|
+
* Accepting a top-level `code` here is deliberate and differs from the Hives function/invocation error path
|
|
48
|
+
* (`hivesErrorDetail` in client.ts), which rejects it because there a top-level `code` belongs to an operation
|
|
49
|
+
* receipt. A top-level `code` can also be an OAuth authorization code echoed by an authorize/token answer, so
|
|
50
|
+
* it is not taken when the body looks like one (`redirect_uri`, `access_token`, `iss`...), beside any string
|
|
51
|
+
* `error`, when the value looks random, or when it is not a one-case code. `errorBody` redacts that `code` in an
|
|
52
|
+
* OAuth-shaped body, beside an OAuth error token (`{error: "invalid_grant", code}`) and when it looks random
|
|
53
|
+
* (16 or more letters and digits). Any other refused `code`, such as one beside a non-OAuth string `error`, is
|
|
54
|
+
* printed in the body but never reported as the error code.
|
|
55
|
+
*/
|
|
56
|
+
function backendCode(body) {
|
|
57
|
+
if (!row(body))
|
|
58
|
+
return undefined;
|
|
59
|
+
const envelope = row(body.error) && typeof body.error.code === 'string' && body.error.code !== '' ? body.error.code : undefined;
|
|
60
|
+
const own = typeof body.code === 'string' && CODE_TOKEN.test(body.code) && ONE_CASE.test(body.code)
|
|
61
|
+
&& typeof body.error !== 'string' && !authorizationCode(body) ? body.code : undefined;
|
|
62
|
+
return envelope !== undefined && !(own && /^UPSTREAM_\d{3}$/.test(envelope)) ? envelope : own;
|
|
63
|
+
}
|
|
37
64
|
const statusType = (s) => s === 401 ? 'unauthorized' : s === 403 ? 'forbidden' : s === 404 ? 'not_found' : s === 429 ? 'rate_limited' : s >= 500 ? 'upstream_error' : 'error';
|
|
38
65
|
/** The gateway catalog fields of an error, for JSON output: `code`, `required_scope` and `docs_url` when the gateway sent them. */
|
|
39
66
|
export function errorCatalog(e) {
|
|
@@ -64,3 +91,11 @@ export function errorOutcome(body) {
|
|
|
64
91
|
/** Optional fields the gateway adds to an envelope; each must be a bounded string when present. */
|
|
65
92
|
export const ENVELOPE_EXTRA_FIELDS = ['type', 'message', 'docs_url', 'layer', 'reason', 'required_scope'];
|
|
66
93
|
export const envelopeExtrasValid = (error) => ENVELOPE_EXTRA_FIELDS.every(name => error[name] === undefined || typeof error[name] === 'string' && error[name].length <= 2048);
|
|
94
|
+
/**
|
|
95
|
+
* The service's error body as the CLI (`--json`) and MCP print it beside the envelope, redacted and bounded
|
|
96
|
+
* by `redactErrorBody` (see redact.ts). Undefined when the SDK kept no body: it deliberately drops bodies
|
|
97
|
+
* that may echo submitted secrets or guest data (Hives secret and function refusals).
|
|
98
|
+
*/
|
|
99
|
+
export function errorBody(e) {
|
|
100
|
+
return redactErrorBody(e.body);
|
|
101
|
+
}
|
package/dist/flares.d.ts
CHANGED
|
@@ -92,6 +92,15 @@ export declare function flaresModule(client: GenesisClient): {
|
|
|
92
92
|
}>;
|
|
93
93
|
};
|
|
94
94
|
presence: {
|
|
95
|
+
list: () => Promise<({
|
|
96
|
+
did?: string;
|
|
97
|
+
status?: string;
|
|
98
|
+
sessionId?: string;
|
|
99
|
+
sessionCount?: number;
|
|
100
|
+
lastSeenAt?: string;
|
|
101
|
+
} & {
|
|
102
|
+
[key: string]: unknown;
|
|
103
|
+
})[]>;
|
|
95
104
|
heartbeat: (body: In<"flares_presenceHeartbeat">["body"], key: string) => Promise<{
|
|
96
105
|
did?: string;
|
|
97
106
|
status?: string;
|
|
@@ -112,6 +121,18 @@ export declare function flaresModule(client: GenesisClient): {
|
|
|
112
121
|
}>;
|
|
113
122
|
};
|
|
114
123
|
trust: {
|
|
124
|
+
list: () => Promise<({
|
|
125
|
+
did?: string;
|
|
126
|
+
score?: number;
|
|
127
|
+
confidence?: number;
|
|
128
|
+
dataPoints?: number;
|
|
129
|
+
computedAt?: string;
|
|
130
|
+
expiresAt?: string;
|
|
131
|
+
trustLevel?: string;
|
|
132
|
+
endorsementCount?: number;
|
|
133
|
+
} & {
|
|
134
|
+
[key: string]: unknown;
|
|
135
|
+
})[]>;
|
|
115
136
|
score: (did: string) => Promise<{
|
|
116
137
|
did?: string;
|
|
117
138
|
score?: number;
|
|
@@ -137,6 +158,17 @@ export declare function flaresModule(client: GenesisClient): {
|
|
|
137
158
|
[key: string]: unknown;
|
|
138
159
|
}>;
|
|
139
160
|
};
|
|
161
|
+
activity: {
|
|
162
|
+
list: (q?: In<"flares_listActivity">) => Promise<({
|
|
163
|
+
id?: string;
|
|
164
|
+
at?: string;
|
|
165
|
+
action?: string;
|
|
166
|
+
resourceId?: string | null;
|
|
167
|
+
actor?: string;
|
|
168
|
+
} & {
|
|
169
|
+
[key: string]: unknown;
|
|
170
|
+
})[]>;
|
|
171
|
+
};
|
|
140
172
|
choreography: {
|
|
141
173
|
workflows: {
|
|
142
174
|
list: () => Promise<{
|
package/dist/flares.js
CHANGED
|
@@ -28,16 +28,21 @@ export function flaresModule(client) {
|
|
|
28
28
|
fail: (id, body, key) => write('flares_failCommitment', { id, body }, key),
|
|
29
29
|
},
|
|
30
30
|
presence: {
|
|
31
|
+
list: () => client.request('flares_listPresence'),
|
|
31
32
|
heartbeat: (body, key) => write('flares_presenceHeartbeat', { body }, key),
|
|
32
33
|
get: (did) => client.request('flares_getPresence', { did }),
|
|
33
34
|
},
|
|
34
35
|
trust: {
|
|
36
|
+
list: () => client.request('flares_listTrust'),
|
|
35
37
|
score: (did) => client.request('flares_getTrustScore', { did }),
|
|
36
38
|
check: (body, key) => write('flares_checkTrust', { body }, key),
|
|
37
39
|
endorsements: (did) => client.request('flares_listEndorsements', { did }),
|
|
38
40
|
endorse: (body, key) => write('flares_createEndorsement', { body }, key),
|
|
39
41
|
revoke: (id, key) => write('flares_revokeEndorsement', { id }, key),
|
|
40
42
|
},
|
|
43
|
+
activity: {
|
|
44
|
+
list: (q = {}) => client.request('flares_listActivity', q),
|
|
45
|
+
},
|
|
41
46
|
choreography: {
|
|
42
47
|
workflows: {
|
|
43
48
|
list: () => client.request('flares_listChoreographyWorkflows'),
|
package/dist/fleets.d.ts
CHANGED
|
@@ -3,6 +3,9 @@ import type { OperationInput } from './types.js';
|
|
|
3
3
|
type TaskInput = NonNullable<OperationInput<'fleets_submitTask'>['body']>;
|
|
4
4
|
type AgentLaunchInput = NonNullable<OperationInput<'fleets_launchAgent'>['body']>;
|
|
5
5
|
type AgentCommandInput = NonNullable<OperationInput<'fleets_commandAgent'>['body']>;
|
|
6
|
+
type AgentUpdateInput = NonNullable<OperationInput<'fleets_updateAgent'>['body']>;
|
|
7
|
+
type AgentLogsQuery = Omit<OperationInput<'fleets_getAgentLogs'>, 'agent_id'>;
|
|
8
|
+
type TaskLogsQuery = Omit<OperationInput<'fleets_getTaskLogs'>, 'task_id'>;
|
|
6
9
|
export interface FleetsInstanceOptions {
|
|
7
10
|
idempotencyKey?: string;
|
|
8
11
|
signal?: AbortSignal;
|
|
@@ -62,6 +65,27 @@ export declare function fleetsModule(client: GenesisClient): {
|
|
|
62
65
|
[key: string]: unknown;
|
|
63
66
|
};
|
|
64
67
|
}>;
|
|
68
|
+
/** Change name, model, instructions or memory. A memory change needs a stopped agent. */
|
|
69
|
+
update: (agent_id: string, body: AgentUpdateInput) => Promise<{
|
|
70
|
+
agent: import("./index.js").components["schemas"]["Fleets.Agent"];
|
|
71
|
+
operation_id: string;
|
|
72
|
+
result?: {
|
|
73
|
+
[key: string]: unknown;
|
|
74
|
+
};
|
|
75
|
+
}>;
|
|
76
|
+
/** Delete a stopped (or recovery_required) idle agent and its log. */
|
|
77
|
+
delete: (agent_id: string) => Promise<{
|
|
78
|
+
id: string;
|
|
79
|
+
deleted: true;
|
|
80
|
+
operation_id: string;
|
|
81
|
+
}>;
|
|
82
|
+
/** Operation log, oldest first; page with after = next_after. */
|
|
83
|
+
logs: (agent_id: string, query?: AgentLogsQuery) => Promise<{
|
|
84
|
+
agent_id: string;
|
|
85
|
+
state: "launching" | "running" | "stopping" | "stopped" | "recovering" | "recovery_required";
|
|
86
|
+
entries: import("./index.js").components["schemas"]["Fleets.AgentLogEntry"][];
|
|
87
|
+
next_after: number | null;
|
|
88
|
+
}>;
|
|
65
89
|
};
|
|
66
90
|
instances: {
|
|
67
91
|
list: (query?: OperationInput<"fleets_listInstances">) => Promise<{
|
|
@@ -181,6 +205,14 @@ export declare function fleetsModule(client: GenesisClient): {
|
|
|
181
205
|
[key: string]: unknown;
|
|
182
206
|
};
|
|
183
207
|
}>;
|
|
208
|
+
/** Lifecycle and output log; poll with after = next_after until complete. */
|
|
209
|
+
logs: (task_id: string, query?: TaskLogsQuery) => Promise<{
|
|
210
|
+
task_id: string;
|
|
211
|
+
state: "pending" | "running" | "cancelling" | "completed" | "failed" | "cancelled" | "timed_out";
|
|
212
|
+
complete: boolean;
|
|
213
|
+
entries: import("./index.js").components["schemas"]["Fleets.TaskLogEntry"][];
|
|
214
|
+
next_after: number | null;
|
|
215
|
+
}>;
|
|
184
216
|
};
|
|
185
217
|
};
|
|
186
218
|
export {};
|
package/dist/fleets.js
CHANGED
|
@@ -46,12 +46,7 @@ function validateAgentLaunch(body) {
|
|
|
46
46
|
if (body.model !== undefined && (!body.model || byteLength(body.model) > 128)) {
|
|
47
47
|
throw invalid('fleets_launchAgent', 'Agent model must contain 1–128 UTF-8 bytes.');
|
|
48
48
|
}
|
|
49
|
-
|
|
50
|
-
if (!['minds', 'files', 'custom'].includes(body.memory.provider))
|
|
51
|
-
throw invalid('fleets_launchAgent', 'Memory provider must be minds, files, or custom.');
|
|
52
|
-
if (body.memory.provider === 'custom' && !body.memory.endpoint)
|
|
53
|
-
throw invalid('fleets_launchAgent', 'Custom memory requires an endpoint.');
|
|
54
|
-
}
|
|
49
|
+
validateMemory('fleets_launchAgent', body.memory);
|
|
55
50
|
if (body.idempotency_key !== undefined && (typeof body.idempotency_key !== 'string' || !/^[!-~]{1,128}$/.test(body.idempotency_key))) {
|
|
56
51
|
throw invalid('fleets_launchAgent', 'Idempotency key must contain 1–128 visible ASCII characters.');
|
|
57
52
|
}
|
|
@@ -61,6 +56,39 @@ function validateAgentCommand(body) {
|
|
|
61
56
|
throw invalid('fleets_commandAgent', 'Agent input must contain 1–65536 UTF-8 bytes without NUL.');
|
|
62
57
|
}
|
|
63
58
|
}
|
|
59
|
+
function validateMemory(operation, memory) {
|
|
60
|
+
if (memory === undefined)
|
|
61
|
+
return;
|
|
62
|
+
if (!memory || !['minds', 'files', 'custom'].includes(memory.provider))
|
|
63
|
+
throw invalid(operation, 'Memory provider must be minds, files, or custom.');
|
|
64
|
+
if (memory.provider === 'custom' && !memory.endpoint)
|
|
65
|
+
throw invalid(operation, 'Custom memory requires an endpoint.');
|
|
66
|
+
}
|
|
67
|
+
function validateAgentUpdate(body) {
|
|
68
|
+
const op = 'fleets_updateAgent';
|
|
69
|
+
if (!body || typeof body !== 'object' || Array.isArray(body))
|
|
70
|
+
throw invalid(op, 'Provide at least one of name, model, instructions or memory.');
|
|
71
|
+
const keys = Object.keys(body);
|
|
72
|
+
if (!keys.length || keys.some(key => !['name', 'model', 'instructions', 'memory'].includes(key))) {
|
|
73
|
+
throw invalid(op, 'Provide at least one of name, model, instructions or memory.');
|
|
74
|
+
}
|
|
75
|
+
if (body.name !== undefined && (typeof body.name !== 'string' || !body.name.trim() || byteLength(body.name) > 128)) {
|
|
76
|
+
throw invalid(op, 'Agent name must contain 1–128 UTF-8 bytes.');
|
|
77
|
+
}
|
|
78
|
+
if (body.model !== undefined && body.model !== null && (typeof body.model !== 'string' || !body.model || byteLength(body.model) > 128)) {
|
|
79
|
+
throw invalid(op, 'Agent model must contain 1–128 UTF-8 bytes, or be null to clear it.');
|
|
80
|
+
}
|
|
81
|
+
if (body.instructions !== undefined && body.instructions !== null
|
|
82
|
+
&& (typeof body.instructions !== 'string' || byteLength(body.instructions) > 65536 || body.instructions.includes('\0'))) {
|
|
83
|
+
throw invalid(op, 'Agent instructions must contain at most 65536 UTF-8 bytes without NUL, or be null to clear them.');
|
|
84
|
+
}
|
|
85
|
+
validateMemory(op, body.memory);
|
|
86
|
+
}
|
|
87
|
+
const cursor = (value, operation, max) => {
|
|
88
|
+
if (value !== undefined && (!Number.isSafeInteger(value) || value < 0 || value > max)) {
|
|
89
|
+
throw invalid(operation, 'after must be a non-negative integer cursor (next_after).');
|
|
90
|
+
}
|
|
91
|
+
};
|
|
64
92
|
/** Managed Henry instances and tasks scoped to the credential's verified org. */
|
|
65
93
|
export function fleetsModule(client) {
|
|
66
94
|
return {
|
|
@@ -87,6 +115,26 @@ export function fleetsModule(client) {
|
|
|
87
115
|
agentId(agent_id, 'fleets_recoverAgent');
|
|
88
116
|
return client.request('fleets_recoverAgent', { agent_id });
|
|
89
117
|
},
|
|
118
|
+
/** Change name, model, instructions or memory. A memory change needs a stopped agent. */
|
|
119
|
+
update: async (agent_id, body) => {
|
|
120
|
+
agentId(agent_id, 'fleets_updateAgent');
|
|
121
|
+
validateAgentUpdate(body);
|
|
122
|
+
return client.request('fleets_updateAgent', { agent_id, body });
|
|
123
|
+
},
|
|
124
|
+
/** Delete a stopped (or recovery_required) idle agent and its log. */
|
|
125
|
+
delete: async (agent_id) => {
|
|
126
|
+
agentId(agent_id, 'fleets_deleteAgent');
|
|
127
|
+
return client.request('fleets_deleteAgent', { agent_id });
|
|
128
|
+
},
|
|
129
|
+
/** Operation log, oldest first; page with after = next_after. */
|
|
130
|
+
logs: async (agent_id, query = {}) => {
|
|
131
|
+
agentId(agent_id, 'fleets_getAgentLogs');
|
|
132
|
+
cursor(query.after, 'fleets_getAgentLogs', Number.MAX_SAFE_INTEGER);
|
|
133
|
+
if (query.limit !== undefined && (!Number.isInteger(query.limit) || query.limit < 1 || query.limit > 200)) {
|
|
134
|
+
throw invalid('fleets_getAgentLogs', 'Limit must be 1–200.');
|
|
135
|
+
}
|
|
136
|
+
return client.request('fleets_getAgentLogs', { agent_id, ...query });
|
|
137
|
+
},
|
|
90
138
|
},
|
|
91
139
|
instances: {
|
|
92
140
|
list: async (query = {}) => {
|
|
@@ -131,6 +179,11 @@ export function fleetsModule(client) {
|
|
|
131
179
|
get: (task_id) => client.request('fleets_getTask', { task_id }),
|
|
132
180
|
cancel: (task_id) => client.request('fleets_cancelTask', { task_id }),
|
|
133
181
|
receipt: (task_id) => client.request('fleets_getTaskReceipt', { task_id }),
|
|
182
|
+
/** Lifecycle and output log; poll with after = next_after until complete. */
|
|
183
|
+
logs: async (task_id, query = {}) => {
|
|
184
|
+
cursor(query.after, 'fleets_getTaskLogs', 4294967295);
|
|
185
|
+
return client.request('fleets_getTaskLogs', { task_id, ...query });
|
|
186
|
+
},
|
|
134
187
|
},
|
|
135
188
|
};
|
|
136
189
|
}
|