@onesi/genesis 0.1.13 → 0.2.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/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 +109 -0
- package/dist/blinds-owner-enrollment.js +21 -2
- package/dist/client.d.ts +3 -19947
- package/dist/client.js +124 -17
- package/dist/errors.js +1 -1
- 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 +285 -1
- package/dist/flocks.js +59 -0
- package/dist/foundry.d.ts +424 -0
- package/dist/foundry.js +133 -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 +54590 -13302
- package/dist/generated/schema.d.ts +69793 -17548
- package/dist/genesis.d.ts +7908 -1260
- package/dist/genesis.js +90 -25
- package/dist/genesis.openapi.json +109121 -29920
- package/dist/henry.d.ts +202 -1
- package/dist/henry.js +223 -0
- package/dist/hives-environment.d.ts +9 -0
- package/dist/hives-environment.js +22 -0
- package/dist/hives-node.js +3 -0
- package/dist/hives-operations.d.ts +114 -0
- 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-sse.d.ts +24 -0
- package/dist/hives-sse.js +111 -0
- package/dist/iam-directory.d.ts +8 -0
- package/dist/iam-directory.js +23 -0
- package/dist/iam.d.ts +391 -0
- package/dist/iam.js +92 -0
- package/dist/idempotency.d.ts +3 -0
- package/dist/idempotency.js +22 -0
- package/dist/index.d.ts +18 -4
- package/dist/index.js +16 -4
- 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/runner.d.ts +4 -0
- package/dist/runner.js +1 -0
- package/dist/scopes.d.ts +310 -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
|
@@ -334,4 +334,118 @@ export declare function hivesOperations(client: GenesisClient): {
|
|
|
334
334
|
updated_at: string;
|
|
335
335
|
}>;
|
|
336
336
|
};
|
|
337
|
+
logs: {
|
|
338
|
+
query: (project_id: string, q?: Omit<In<"hives_query_logs">, "project_id">) => Promise<{
|
|
339
|
+
logs: import("./index.js").components["schemas"]["Hives.LogEntry"][];
|
|
340
|
+
pagination: import("./index.js").components["schemas"]["Hives.PaginationMeta"];
|
|
341
|
+
time_range: import("./index.js").components["schemas"]["Hives.TimeRange"];
|
|
342
|
+
}>;
|
|
343
|
+
stream: (project_id: string, q?: import("./hives-platform.js").HivesLogStreamQuery, init?: {
|
|
344
|
+
signal?: AbortSignal;
|
|
345
|
+
}) => AsyncGenerator<import("./hives-platform.js").HivesLogStreamEvent, void, any>;
|
|
346
|
+
window: (project_id: string, q?: import("./hives-platform.js").HivesLogStreamQuery, options?: import("./hives-platform.js").HivesLogWindowOptions) => Promise<import("./hives-platform.js").HivesLogWindow>;
|
|
347
|
+
};
|
|
348
|
+
domains: {
|
|
349
|
+
list: (project_id: string, q?: Omit<In<"hives_list_domains">, "project_id">) => Promise<{
|
|
350
|
+
data: {
|
|
351
|
+
created_at: string;
|
|
352
|
+
dns_status: string;
|
|
353
|
+
domain: string;
|
|
354
|
+
domain_type: import("./index.js").components["schemas"]["Hives.DomainType"];
|
|
355
|
+
id: string;
|
|
356
|
+
ssl_expires_at?: string | null;
|
|
357
|
+
ssl_status: string;
|
|
358
|
+
verification_record?: null | import("./index.js").components["schemas"]["Hives.VerificationRecord"];
|
|
359
|
+
}[];
|
|
360
|
+
pagination: import("./index.js").components["schemas"]["Hives.PaginationMeta"];
|
|
361
|
+
}>;
|
|
362
|
+
add: (project_id: string, body: In<"hives_add_domain">["body"]) => Promise<{
|
|
363
|
+
created_at: string;
|
|
364
|
+
dns_status: string;
|
|
365
|
+
domain: string;
|
|
366
|
+
domain_type: import("./index.js").components["schemas"]["Hives.DomainType"];
|
|
367
|
+
id: string;
|
|
368
|
+
ssl_expires_at?: string | null;
|
|
369
|
+
ssl_status: string;
|
|
370
|
+
verification_record?: null | import("./index.js").components["schemas"]["Hives.VerificationRecord"];
|
|
371
|
+
}>;
|
|
372
|
+
verify: (project_id: string, domain_id: string) => Promise<{
|
|
373
|
+
checked_at: string;
|
|
374
|
+
dns_status: string;
|
|
375
|
+
error?: string | null;
|
|
376
|
+
verified: boolean;
|
|
377
|
+
}>;
|
|
378
|
+
provisionSsl: (project_id: string, domain_id: string) => Promise<{
|
|
379
|
+
error?: string | null;
|
|
380
|
+
expires_at?: string | null;
|
|
381
|
+
ssl_status: string;
|
|
382
|
+
started: boolean;
|
|
383
|
+
}>;
|
|
384
|
+
};
|
|
385
|
+
auth: {
|
|
386
|
+
me: () => Promise<{
|
|
387
|
+
avatar?: string | null;
|
|
388
|
+
createdAt: string;
|
|
389
|
+
email: string;
|
|
390
|
+
id: string;
|
|
391
|
+
name: string;
|
|
392
|
+
organizationId?: string | null;
|
|
393
|
+
role: string;
|
|
394
|
+
}>;
|
|
395
|
+
};
|
|
396
|
+
fleets: {
|
|
397
|
+
list: () => Promise<{
|
|
398
|
+
fleets: import("./index.js").components["schemas"]["Hives.FleetStatusView"][];
|
|
399
|
+
}>;
|
|
400
|
+
deploy: (body: In<"hives_deploy_fleet">["body"]) => Promise<{
|
|
401
|
+
fleetId: string;
|
|
402
|
+
gatewayEndpoint?: string | null;
|
|
403
|
+
totalInstances: number;
|
|
404
|
+
}>;
|
|
405
|
+
status: (fleet_id: string) => Promise<{
|
|
406
|
+
fleetId: string;
|
|
407
|
+
gateway?: null | import("./index.js").components["schemas"]["Hives.GatewaySummary"];
|
|
408
|
+
healthyInstances: number;
|
|
409
|
+
name: string;
|
|
410
|
+
protocols: import("./index.js").components["schemas"]["Hives.ProtocolStatusView"][];
|
|
411
|
+
state: string;
|
|
412
|
+
totalInstances: number;
|
|
413
|
+
}>;
|
|
414
|
+
teardown: (fleet_id: string) => Promise<void>;
|
|
415
|
+
};
|
|
416
|
+
remote: {
|
|
417
|
+
sessions: {
|
|
418
|
+
list: (project_id: string, q?: Omit<In<"hives_list_remote_sessions">, "project_id">) => Promise<Record<string, never>>;
|
|
419
|
+
close: (project_id: string, session_id: string) => Promise<Record<string, never>>;
|
|
420
|
+
};
|
|
421
|
+
ssh: (project_id: string, body: In<"hives_create_ssh_session">["body"]) => Promise<{
|
|
422
|
+
expires_at: string;
|
|
423
|
+
fingerprint: string;
|
|
424
|
+
host: string;
|
|
425
|
+
port: number;
|
|
426
|
+
session_id: string;
|
|
427
|
+
username: string;
|
|
428
|
+
}>;
|
|
429
|
+
terminal: (project_id: string, body: In<"hives_create_terminal_session">["body"]) => Promise<{
|
|
430
|
+
expires_at: string;
|
|
431
|
+
session_id: string;
|
|
432
|
+
token: string;
|
|
433
|
+
websocket_url: string;
|
|
434
|
+
}>;
|
|
435
|
+
debug: (project_id: string, body: In<"hives_create_debug_session">["body"]) => Promise<{
|
|
436
|
+
debug_url: string;
|
|
437
|
+
expires_at: string;
|
|
438
|
+
protocol: string;
|
|
439
|
+
session_id: string;
|
|
440
|
+
token: string;
|
|
441
|
+
}>;
|
|
442
|
+
sftp: {
|
|
443
|
+
list: (project_id: string, target_id: string, q?: {
|
|
444
|
+
path?: string;
|
|
445
|
+
}) => Promise<Record<string, never>>;
|
|
446
|
+
read: (project_id: string, target_id: string, path: string) => Promise<Record<string, never>>;
|
|
447
|
+
write: (project_id: string, target_id: string, body: In<"hives_sftp_write">["body"]) => Promise<Record<string, never>>;
|
|
448
|
+
delete: (project_id: string, target_id: string, body: In<"hives_sftp_delete">["body"]) => Promise<Record<string, never>>;
|
|
449
|
+
};
|
|
450
|
+
};
|
|
337
451
|
};
|
package/dist/hives-operations.js
CHANGED
|
@@ -2,9 +2,11 @@ import { hivesTopics } from './hives-topics.js';
|
|
|
2
2
|
import { hivesCells } from './hives-cells.js';
|
|
3
3
|
import { hivesQueues } from './hives-queues.js';
|
|
4
4
|
import { hivesRuntimes } from './hives-runtimes.js';
|
|
5
|
+
import { hivesPlatform } from './hives-platform.js';
|
|
5
6
|
/** The original Hives scheduler, settings and measured activity use the same gateway and identity. */
|
|
6
7
|
export function hivesOperations(client) {
|
|
7
8
|
return {
|
|
9
|
+
...hivesPlatform(client),
|
|
8
10
|
cells: hivesCells(client),
|
|
9
11
|
queues: hivesQueues(client),
|
|
10
12
|
topics: hivesTopics(client),
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import type { GenesisClient } from './client.js';
|
|
2
|
+
import type { OperationInput as In } from './types.js';
|
|
3
|
+
/** One Server-Sent Event from `GET /v1/hives/projects/{project_id}/logs/stream`. `data` is parsed JSON when it is JSON. */
|
|
4
|
+
export interface HivesLogStreamEvent {
|
|
5
|
+
event: string;
|
|
6
|
+
id?: string;
|
|
7
|
+
data: unknown;
|
|
8
|
+
}
|
|
9
|
+
export type HivesLogStreamQuery = Omit<In<'hives_stream_logs'>, 'project_id'>;
|
|
10
|
+
export interface HivesLogWindowOptions {
|
|
11
|
+
/** Stop after this many events (1–10000, default 100). */
|
|
12
|
+
maxEvents?: number;
|
|
13
|
+
/** Stop after this many seconds (1–300, default 10). */
|
|
14
|
+
seconds?: number;
|
|
15
|
+
signal?: AbortSignal;
|
|
16
|
+
}
|
|
17
|
+
export interface HivesLogWindow {
|
|
18
|
+
events: HivesLogStreamEvent[];
|
|
19
|
+
ended: 'max_events' | 'time' | 'closed';
|
|
20
|
+
}
|
|
21
|
+
/** Pull-based SSE reader. `cancel()` closes the connection even while a read is pending. */
|
|
22
|
+
export declare function hivesSseReader(response: Response): {
|
|
23
|
+
next(): Promise<HivesLogStreamEvent | undefined>;
|
|
24
|
+
cancel(): Promise<void>;
|
|
25
|
+
};
|
|
26
|
+
/** Yield SSE events until the caller stops iterating, the signal aborts or the server closes the stream. */
|
|
27
|
+
export declare function hivesSseEvents(response: Response): AsyncGenerator<HivesLogStreamEvent>;
|
|
28
|
+
/**
|
|
29
|
+
* Hives platform reads and actions the gateway already routes: project logs (query and live SSE), custom domains,
|
|
30
|
+
* the Hives identity, Hives fleets and remote sessions/SFTP.
|
|
31
|
+
*/
|
|
32
|
+
export declare function hivesPlatform(client: GenesisClient): {
|
|
33
|
+
logs: {
|
|
34
|
+
query: (project_id: string, q?: Omit<In<"hives_query_logs">, "project_id">) => Promise<{
|
|
35
|
+
logs: import("./index.js").components["schemas"]["Hives.LogEntry"][];
|
|
36
|
+
pagination: import("./index.js").components["schemas"]["Hives.PaginationMeta"];
|
|
37
|
+
time_range: import("./index.js").components["schemas"]["Hives.TimeRange"];
|
|
38
|
+
}>;
|
|
39
|
+
/** Live SSE events. Iterate and `break` (or abort the signal) to close the connection; it never ends on its own. */
|
|
40
|
+
stream: (project_id: string, q?: HivesLogStreamQuery, init?: {
|
|
41
|
+
signal?: AbortSignal;
|
|
42
|
+
}) => AsyncGenerator<HivesLogStreamEvent, void, any>;
|
|
43
|
+
/** A bounded window of the live stream: the first `maxEvents` events or `seconds` of stream, whichever comes first. */
|
|
44
|
+
window: (project_id: string, q?: HivesLogStreamQuery, options?: HivesLogWindowOptions) => Promise<HivesLogWindow>;
|
|
45
|
+
};
|
|
46
|
+
domains: {
|
|
47
|
+
list: (project_id: string, q?: Omit<In<"hives_list_domains">, "project_id">) => Promise<{
|
|
48
|
+
data: {
|
|
49
|
+
created_at: string;
|
|
50
|
+
dns_status: string;
|
|
51
|
+
domain: string;
|
|
52
|
+
domain_type: import("./index.js").components["schemas"]["Hives.DomainType"];
|
|
53
|
+
id: string;
|
|
54
|
+
ssl_expires_at?: string | null;
|
|
55
|
+
ssl_status: string;
|
|
56
|
+
verification_record?: null | import("./index.js").components["schemas"]["Hives.VerificationRecord"];
|
|
57
|
+
}[];
|
|
58
|
+
pagination: import("./index.js").components["schemas"]["Hives.PaginationMeta"];
|
|
59
|
+
}>;
|
|
60
|
+
add: (project_id: string, body: In<"hives_add_domain">["body"]) => Promise<{
|
|
61
|
+
created_at: string;
|
|
62
|
+
dns_status: string;
|
|
63
|
+
domain: string;
|
|
64
|
+
domain_type: import("./index.js").components["schemas"]["Hives.DomainType"];
|
|
65
|
+
id: string;
|
|
66
|
+
ssl_expires_at?: string | null;
|
|
67
|
+
ssl_status: string;
|
|
68
|
+
verification_record?: null | import("./index.js").components["schemas"]["Hives.VerificationRecord"];
|
|
69
|
+
}>;
|
|
70
|
+
verify: (project_id: string, domain_id: string) => Promise<{
|
|
71
|
+
checked_at: string;
|
|
72
|
+
dns_status: string;
|
|
73
|
+
error?: string | null;
|
|
74
|
+
verified: boolean;
|
|
75
|
+
}>;
|
|
76
|
+
provisionSsl: (project_id: string, domain_id: string) => Promise<{
|
|
77
|
+
error?: string | null;
|
|
78
|
+
expires_at?: string | null;
|
|
79
|
+
ssl_status: string;
|
|
80
|
+
started: boolean;
|
|
81
|
+
}>;
|
|
82
|
+
};
|
|
83
|
+
auth: {
|
|
84
|
+
me: () => Promise<{
|
|
85
|
+
avatar?: string | null;
|
|
86
|
+
createdAt: string;
|
|
87
|
+
email: string;
|
|
88
|
+
id: string;
|
|
89
|
+
name: string;
|
|
90
|
+
organizationId?: string | null;
|
|
91
|
+
role: string;
|
|
92
|
+
}>;
|
|
93
|
+
};
|
|
94
|
+
fleets: {
|
|
95
|
+
list: () => Promise<{
|
|
96
|
+
fleets: import("./index.js").components["schemas"]["Hives.FleetStatusView"][];
|
|
97
|
+
}>;
|
|
98
|
+
deploy: (body: In<"hives_deploy_fleet">["body"]) => Promise<{
|
|
99
|
+
fleetId: string;
|
|
100
|
+
gatewayEndpoint?: string | null;
|
|
101
|
+
totalInstances: number;
|
|
102
|
+
}>;
|
|
103
|
+
status: (fleet_id: string) => Promise<{
|
|
104
|
+
fleetId: string;
|
|
105
|
+
gateway?: null | import("./index.js").components["schemas"]["Hives.GatewaySummary"];
|
|
106
|
+
healthyInstances: number;
|
|
107
|
+
name: string;
|
|
108
|
+
protocols: import("./index.js").components["schemas"]["Hives.ProtocolStatusView"][];
|
|
109
|
+
state: string;
|
|
110
|
+
totalInstances: number;
|
|
111
|
+
}>;
|
|
112
|
+
teardown: (fleet_id: string) => Promise<void>;
|
|
113
|
+
};
|
|
114
|
+
remote: {
|
|
115
|
+
sessions: {
|
|
116
|
+
list: (project_id: string, q?: Omit<In<"hives_list_remote_sessions">, "project_id">) => Promise<Record<string, never>>;
|
|
117
|
+
close: (project_id: string, session_id: string) => Promise<Record<string, never>>;
|
|
118
|
+
};
|
|
119
|
+
ssh: (project_id: string, body: In<"hives_create_ssh_session">["body"]) => Promise<{
|
|
120
|
+
expires_at: string;
|
|
121
|
+
fingerprint: string;
|
|
122
|
+
host: string;
|
|
123
|
+
port: number;
|
|
124
|
+
session_id: string;
|
|
125
|
+
username: string;
|
|
126
|
+
}>;
|
|
127
|
+
terminal: (project_id: string, body: In<"hives_create_terminal_session">["body"]) => Promise<{
|
|
128
|
+
expires_at: string;
|
|
129
|
+
session_id: string;
|
|
130
|
+
token: string;
|
|
131
|
+
websocket_url: string;
|
|
132
|
+
}>;
|
|
133
|
+
debug: (project_id: string, body: In<"hives_create_debug_session">["body"]) => Promise<{
|
|
134
|
+
debug_url: string;
|
|
135
|
+
expires_at: string;
|
|
136
|
+
protocol: string;
|
|
137
|
+
session_id: string;
|
|
138
|
+
token: string;
|
|
139
|
+
}>;
|
|
140
|
+
sftp: {
|
|
141
|
+
list: (project_id: string, target_id: string, q?: {
|
|
142
|
+
path?: string;
|
|
143
|
+
}) => Promise<Record<string, never>>;
|
|
144
|
+
read: (project_id: string, target_id: string, path: string) => Promise<Record<string, never>>;
|
|
145
|
+
write: (project_id: string, target_id: string, body: In<"hives_sftp_write">["body"]) => Promise<Record<string, never>>;
|
|
146
|
+
delete: (project_id: string, target_id: string, body: In<"hives_sftp_delete">["body"]) => Promise<Record<string, never>>;
|
|
147
|
+
};
|
|
148
|
+
};
|
|
149
|
+
};
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { GenesisError } from './errors.js';
|
|
2
|
+
const bounded = (value, fallback, max, name) => {
|
|
3
|
+
if (value === undefined)
|
|
4
|
+
return fallback;
|
|
5
|
+
if (!Number.isInteger(value) || value < 1 || value > max) {
|
|
6
|
+
throw new GenesisError({ status: 0, type: 'invalid_request', operationId: 'hives_stream_logs', message: `${name} must be an integer from 1 to ${max}.` });
|
|
7
|
+
}
|
|
8
|
+
return value;
|
|
9
|
+
};
|
|
10
|
+
function parseEvent(lines) {
|
|
11
|
+
let event = 'message';
|
|
12
|
+
let id;
|
|
13
|
+
const data = [];
|
|
14
|
+
for (const line of lines) {
|
|
15
|
+
if (!line || line.startsWith(':'))
|
|
16
|
+
continue; // SSE comments carry keepalives only.
|
|
17
|
+
const colon = line.indexOf(':');
|
|
18
|
+
const field = colon < 0 ? line : line.slice(0, colon);
|
|
19
|
+
let value = colon < 0 ? '' : line.slice(colon + 1);
|
|
20
|
+
if (value.startsWith(' '))
|
|
21
|
+
value = value.slice(1);
|
|
22
|
+
if (field === 'event')
|
|
23
|
+
event = value;
|
|
24
|
+
else if (field === 'id')
|
|
25
|
+
id = value;
|
|
26
|
+
else if (field === 'data')
|
|
27
|
+
data.push(value);
|
|
28
|
+
}
|
|
29
|
+
if (!data.length)
|
|
30
|
+
return undefined;
|
|
31
|
+
const text = data.join('\n');
|
|
32
|
+
let parsed = text;
|
|
33
|
+
try {
|
|
34
|
+
parsed = JSON.parse(text);
|
|
35
|
+
}
|
|
36
|
+
catch { /* non-JSON data such as heartbeat "ping" stays text */ }
|
|
37
|
+
return { event, ...(id === undefined ? {} : { id }), data: parsed };
|
|
38
|
+
}
|
|
39
|
+
/** Pull-based SSE reader. `cancel()` closes the connection even while a read is pending. */
|
|
40
|
+
export function hivesSseReader(response) {
|
|
41
|
+
const reader = response.body?.getReader();
|
|
42
|
+
const decoder = new TextDecoder();
|
|
43
|
+
let buffer = '';
|
|
44
|
+
let lines = [];
|
|
45
|
+
let finished = !reader;
|
|
46
|
+
const take = () => {
|
|
47
|
+
let index;
|
|
48
|
+
while ((index = buffer.search(/\r\n|\r|\n/)) >= 0) {
|
|
49
|
+
const line = buffer.slice(0, index);
|
|
50
|
+
buffer = buffer.slice(index + (buffer.startsWith('\r\n', index) ? 2 : 1));
|
|
51
|
+
if (line !== '') {
|
|
52
|
+
lines.push(line);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const event = parseEvent(lines);
|
|
56
|
+
lines = [];
|
|
57
|
+
if (event)
|
|
58
|
+
return event;
|
|
59
|
+
}
|
|
60
|
+
return undefined;
|
|
61
|
+
};
|
|
62
|
+
return {
|
|
63
|
+
async next() {
|
|
64
|
+
for (;;) {
|
|
65
|
+
const ready = take();
|
|
66
|
+
if (ready)
|
|
67
|
+
return ready;
|
|
68
|
+
if (finished) {
|
|
69
|
+
const tail = parseEvent(buffer ? [...lines, buffer] : lines);
|
|
70
|
+
buffer = '';
|
|
71
|
+
lines = [];
|
|
72
|
+
return tail;
|
|
73
|
+
}
|
|
74
|
+
const { done, value } = await reader.read();
|
|
75
|
+
if (done)
|
|
76
|
+
finished = true;
|
|
77
|
+
else
|
|
78
|
+
buffer += decoder.decode(value, { stream: true });
|
|
79
|
+
}
|
|
80
|
+
},
|
|
81
|
+
async cancel() { finished = true; await reader?.cancel().catch(() => { }); },
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/** Yield SSE events until the caller stops iterating, the signal aborts or the server closes the stream. */
|
|
85
|
+
export async function* hivesSseEvents(response) {
|
|
86
|
+
const sse = hivesSseReader(response);
|
|
87
|
+
try {
|
|
88
|
+
for (let event = await sse.next(); event; event = await sse.next())
|
|
89
|
+
yield event;
|
|
90
|
+
}
|
|
91
|
+
finally {
|
|
92
|
+
await sse.cancel();
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Hives platform reads and actions the gateway already routes: project logs (query and live SSE), custom domains,
|
|
97
|
+
* the Hives identity, Hives fleets and remote sessions/SFTP.
|
|
98
|
+
*/
|
|
99
|
+
export function hivesPlatform(client) {
|
|
100
|
+
const open = async (project_id, q = {}, init = {}) => {
|
|
101
|
+
const res = await client.raw('hives_stream_logs', { ...q, project_id }, { signal: init.signal, headers: { accept: 'text/event-stream' } });
|
|
102
|
+
if (!(res.headers.get('content-type') ?? '').toLowerCase().startsWith('text/event-stream')) {
|
|
103
|
+
void res.body?.cancel().catch(() => { });
|
|
104
|
+
throw new GenesisError({ status: res.status, type: 'invalid_response', operationId: 'hives_stream_logs', message: 'Hives did not return a Server-Sent Events stream.',
|
|
105
|
+
requestId: res.headers.get('x-request-id') ?? undefined });
|
|
106
|
+
}
|
|
107
|
+
return res;
|
|
108
|
+
};
|
|
109
|
+
return {
|
|
110
|
+
logs: {
|
|
111
|
+
query: (project_id, q = {}) => client.request('hives_query_logs', { ...q, project_id }),
|
|
112
|
+
/** Live SSE events. Iterate and `break` (or abort the signal) to close the connection; it never ends on its own. */
|
|
113
|
+
stream: async function* (project_id, q = {}, init = {}) {
|
|
114
|
+
yield* hivesSseEvents(await open(project_id, q, init));
|
|
115
|
+
},
|
|
116
|
+
/** A bounded window of the live stream: the first `maxEvents` events or `seconds` of stream, whichever comes first. */
|
|
117
|
+
window: async (project_id, q = {}, options = {}) => {
|
|
118
|
+
const maxEvents = bounded(options.maxEvents, 100, 10_000, 'maxEvents');
|
|
119
|
+
const seconds = bounded(options.seconds, 10, 300, 'seconds');
|
|
120
|
+
const events = [];
|
|
121
|
+
// The window starts before the request, so a server that withholds headers cannot outlast it.
|
|
122
|
+
const deadline = new AbortController();
|
|
123
|
+
let timer;
|
|
124
|
+
const elapsed = new Promise((resolve) => { timer = setTimeout(() => { resolve('time'); deadline.abort(); }, seconds * 1000); });
|
|
125
|
+
const signal = options.signal ? AbortSignal.any([options.signal, deadline.signal]) : deadline.signal;
|
|
126
|
+
let sse;
|
|
127
|
+
try {
|
|
128
|
+
const response = await Promise.race([open(project_id, q, { signal }), elapsed]);
|
|
129
|
+
if (response === 'time')
|
|
130
|
+
return { events, ended: 'time' };
|
|
131
|
+
sse = hivesSseReader(response);
|
|
132
|
+
for (;;) {
|
|
133
|
+
const next = await Promise.race([sse.next(), elapsed]);
|
|
134
|
+
if (next === 'time')
|
|
135
|
+
return { events, ended: 'time' };
|
|
136
|
+
if (!next)
|
|
137
|
+
return { events, ended: 'closed' };
|
|
138
|
+
events.push(next);
|
|
139
|
+
if (events.length >= maxEvents)
|
|
140
|
+
return { events, ended: 'max_events' };
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
catch (error) {
|
|
144
|
+
if (deadline.signal.aborted && !options.signal?.aborted)
|
|
145
|
+
return { events, ended: 'time' };
|
|
146
|
+
throw error;
|
|
147
|
+
}
|
|
148
|
+
finally {
|
|
149
|
+
clearTimeout(timer);
|
|
150
|
+
await sse?.cancel();
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
},
|
|
154
|
+
domains: {
|
|
155
|
+
list: (project_id, q = {}) => client.request('hives_list_domains', { ...q, project_id }),
|
|
156
|
+
add: (project_id, body) => client.request('hives_add_domain', { project_id, body }),
|
|
157
|
+
verify: (project_id, domain_id) => client.request('hives_verify_domain', { project_id, domain_id }),
|
|
158
|
+
provisionSsl: (project_id, domain_id) => client.request('hives_provision_ssl', { project_id, domain_id }),
|
|
159
|
+
},
|
|
160
|
+
auth: { me: () => client.request('hives_me') },
|
|
161
|
+
fleets: {
|
|
162
|
+
list: () => client.request('hives_list_fleets'),
|
|
163
|
+
deploy: (body) => client.request('hives_deploy_fleet', { body }),
|
|
164
|
+
status: (fleet_id) => client.request('hives_get_fleet_status', { fleet_id }),
|
|
165
|
+
teardown: (fleet_id) => client.request('hives_teardown_fleet', { fleet_id }),
|
|
166
|
+
},
|
|
167
|
+
remote: {
|
|
168
|
+
sessions: {
|
|
169
|
+
list: (project_id, q = {}) => client.request('hives_list_remote_sessions', { ...q, project_id }),
|
|
170
|
+
close: (project_id, session_id) => client.request('hives_close_remote_session', { project_id, session_id }),
|
|
171
|
+
},
|
|
172
|
+
ssh: (project_id, body) => client.request('hives_create_ssh_session', { project_id, body }),
|
|
173
|
+
terminal: (project_id, body) => client.request('hives_create_terminal_session', { project_id, body }),
|
|
174
|
+
debug: (project_id, body) => client.request('hives_create_debug_session', { project_id, body }),
|
|
175
|
+
sftp: {
|
|
176
|
+
list: (project_id, target_id, q = {}) => client.request('hives_sftp_list', { ...q, project_id, target_id }),
|
|
177
|
+
read: (project_id, target_id, path) => client.request('hives_sftp_read', { project_id, target_id, path }),
|
|
178
|
+
write: (project_id, target_id, body) => client.request('hives_sftp_write', { project_id, target_id, body }),
|
|
179
|
+
delete: (project_id, target_id, body) => client.request('hives_sftp_delete', { project_id, target_id, body }),
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
};
|
|
183
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** One Server-Sent Event. `data` is parsed JSON when it is JSON, otherwise the raw text. */
|
|
2
|
+
export interface HivesSseEvent {
|
|
3
|
+
event: string;
|
|
4
|
+
id?: string;
|
|
5
|
+
data: unknown;
|
|
6
|
+
}
|
|
7
|
+
export interface HivesSseWindowOptions {
|
|
8
|
+
/** Stop after this many events (1-1000, default 100). */
|
|
9
|
+
maxEvents?: number;
|
|
10
|
+
/** Stop after this many seconds (1-15, default 10). The CLI and MCP Hives node transport allows a body 20 s, so a window stays below it. */
|
|
11
|
+
seconds?: number;
|
|
12
|
+
signal?: AbortSignal;
|
|
13
|
+
}
|
|
14
|
+
/** A bounded read of an endless stream; `ended` says which bound closed it. */
|
|
15
|
+
export interface HivesSseWindow {
|
|
16
|
+
events: HivesSseEvent[];
|
|
17
|
+
ended: 'max_events' | 'time' | 'closed' | 'aborted';
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Read one bounded window of a Server-Sent Events response (for example
|
|
21
|
+
* `await genesis.raw('hives_stream_deployment_logs', {...})`) and close the connection.
|
|
22
|
+
* Never buffers the whole body: an SSE stream does not end on its own.
|
|
23
|
+
*/
|
|
24
|
+
export declare function hivesSseWindow(response: Response, options?: HivesSseWindowOptions): Promise<HivesSseWindow>;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { GenesisError } from './errors.js';
|
|
2
|
+
const bound = (value, fallback, max, name) => {
|
|
3
|
+
if (value === undefined)
|
|
4
|
+
return fallback;
|
|
5
|
+
if (!Number.isInteger(value) || value < 1 || value > max) {
|
|
6
|
+
throw new GenesisError({ status: 0, type: 'invalid_request', message: `${name} must be an integer from 1 to ${max}.` });
|
|
7
|
+
}
|
|
8
|
+
return value;
|
|
9
|
+
};
|
|
10
|
+
function parse(lines) {
|
|
11
|
+
let event = 'message';
|
|
12
|
+
let id;
|
|
13
|
+
const data = [];
|
|
14
|
+
for (const line of lines) {
|
|
15
|
+
if (!line || line.startsWith(':'))
|
|
16
|
+
continue; // comments carry keepalives only
|
|
17
|
+
const colon = line.indexOf(':');
|
|
18
|
+
const field = colon < 0 ? line : line.slice(0, colon);
|
|
19
|
+
let value = colon < 0 ? '' : line.slice(colon + 1);
|
|
20
|
+
if (value.startsWith(' '))
|
|
21
|
+
value = value.slice(1);
|
|
22
|
+
if (field === 'event')
|
|
23
|
+
event = value;
|
|
24
|
+
else if (field === 'id')
|
|
25
|
+
id = value;
|
|
26
|
+
else if (field === 'data')
|
|
27
|
+
data.push(value);
|
|
28
|
+
}
|
|
29
|
+
if (!data.length)
|
|
30
|
+
return undefined;
|
|
31
|
+
const text = data.join('\n');
|
|
32
|
+
let value = text;
|
|
33
|
+
try {
|
|
34
|
+
value = JSON.parse(text);
|
|
35
|
+
}
|
|
36
|
+
catch { /* keepalive text such as "ping" */ }
|
|
37
|
+
return { event, ...(id === undefined ? {} : { id }), data: value };
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Read one bounded window of a Server-Sent Events response (for example
|
|
41
|
+
* `await genesis.raw('hives_stream_deployment_logs', {...})`) and close the connection.
|
|
42
|
+
* Never buffers the whole body: an SSE stream does not end on its own.
|
|
43
|
+
*/
|
|
44
|
+
export async function hivesSseWindow(response, options = {}) {
|
|
45
|
+
const maxEvents = bound(options.maxEvents, 100, 1000, 'maxEvents');
|
|
46
|
+
const seconds = bound(options.seconds, 10, 15, 'seconds');
|
|
47
|
+
const reader = response.body?.getReader();
|
|
48
|
+
const events = [];
|
|
49
|
+
if (!reader)
|
|
50
|
+
return { events, ended: 'closed' };
|
|
51
|
+
const decoder = new TextDecoder();
|
|
52
|
+
let buffer = '';
|
|
53
|
+
let lines = [];
|
|
54
|
+
let ended;
|
|
55
|
+
const stop = (why) => { ended ??= why; void reader.cancel().catch(() => { }); };
|
|
56
|
+
const timer = setTimeout(() => stop('time'), seconds * 1000);
|
|
57
|
+
const abort = () => stop('aborted');
|
|
58
|
+
options.signal?.addEventListener('abort', abort, { once: true });
|
|
59
|
+
if (options.signal?.aborted)
|
|
60
|
+
abort();
|
|
61
|
+
try {
|
|
62
|
+
while (!ended) {
|
|
63
|
+
let chunk;
|
|
64
|
+
try {
|
|
65
|
+
chunk = await reader.read();
|
|
66
|
+
}
|
|
67
|
+
catch (error) {
|
|
68
|
+
if (ended)
|
|
69
|
+
break;
|
|
70
|
+
throw error;
|
|
71
|
+
}
|
|
72
|
+
if (ended)
|
|
73
|
+
break;
|
|
74
|
+
if (chunk.done) {
|
|
75
|
+
buffer += decoder.decode();
|
|
76
|
+
ended = 'closed';
|
|
77
|
+
}
|
|
78
|
+
else
|
|
79
|
+
buffer += decoder.decode(chunk.value, { stream: true });
|
|
80
|
+
let index;
|
|
81
|
+
while ((index = buffer.search(/\r\n|\r|\n/)) >= 0) {
|
|
82
|
+
const line = buffer.slice(0, index);
|
|
83
|
+
buffer = buffer.slice(index + (buffer.startsWith('\r\n', index) ? 2 : 1));
|
|
84
|
+
if (line !== '') {
|
|
85
|
+
lines.push(line);
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
const event = parse(lines);
|
|
89
|
+
lines = [];
|
|
90
|
+
if (event)
|
|
91
|
+
events.push(event);
|
|
92
|
+
if (events.length >= maxEvents) {
|
|
93
|
+
stop('max_events');
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (ended === 'closed' && events.length < maxEvents) {
|
|
98
|
+
const tail = parse(buffer ? [...lines, buffer] : lines);
|
|
99
|
+
if (tail)
|
|
100
|
+
events.push(tail);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
clearTimeout(timer);
|
|
106
|
+
options.signal?.removeEventListener('abort', abort);
|
|
107
|
+
if (ended !== 'closed')
|
|
108
|
+
void reader.cancel().catch(() => { });
|
|
109
|
+
}
|
|
110
|
+
return { events, ended: ended ?? 'closed' };
|
|
111
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const iamDirectoryOperations: readonly string[];
|
|
2
|
+
export declare const isIamDirectoryOperation: (operationId: string) => boolean;
|
|
3
|
+
/**
|
|
4
|
+
* The actionable form of a directory refusal; any other error is returned unchanged. The gateway, not the client, exchanges
|
|
5
|
+
* the One account session for an organization context at Auth and forwards the short-lived child to IAM. The client sends
|
|
6
|
+
* one request, never changes a session or its selected context, and only explains what came back.
|
|
7
|
+
*/
|
|
8
|
+
export declare function iamDirectoryFailure(e: unknown, org: string, operationId: string): unknown;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { GenesisError, errorCatalog } from './errors.js';
|
|
2
|
+
/** The IAM v4 organization directory: projects and workspaces at /v1/iam/orgs/{org_id}/... */
|
|
3
|
+
const READS = ['iam_listProjects', 'iam_getProject', 'iam_listWorkspaces', 'iam_getWorkspace'];
|
|
4
|
+
const WRITES = ['iam_createProject', 'iam_updateProject', 'iam_deleteProject', 'iam_createWorkspace', 'iam_updateWorkspace', 'iam_deleteWorkspace'];
|
|
5
|
+
export const iamDirectoryOperations = [...READS, ...WRITES];
|
|
6
|
+
export const isIamDirectoryOperation = (operationId) => iamDirectoryOperations.includes(operationId);
|
|
7
|
+
const codeOf = (e) => errorCatalog(e).code?.toUpperCase();
|
|
8
|
+
/**
|
|
9
|
+
* The actionable form of a directory refusal; any other error is returned unchanged. The gateway, not the client, exchanges
|
|
10
|
+
* the One account session for an organization context at Auth and forwards the short-lived child to IAM. The client sends
|
|
11
|
+
* one request, never changes a session or its selected context, and only explains what came back.
|
|
12
|
+
*/
|
|
13
|
+
export function iamDirectoryFailure(e, org, operationId) {
|
|
14
|
+
if (!(e instanceof GenesisError))
|
|
15
|
+
return e;
|
|
16
|
+
const keep = (message) => new GenesisError({ status: e.status, type: e.type, code: e.code, requestId: e.requestId, operationId, body: e.body, message });
|
|
17
|
+
const code = codeOf(e);
|
|
18
|
+
if (code === 'TENANT_REQUIRED')
|
|
19
|
+
return keep(`IAM sees no organization on your One account session for ${org}. Use the Console to manage projects and workspaces for now.`);
|
|
20
|
+
if (WRITES.includes(operationId) && code === 'SCOPE_MISSING' && errorCatalog(e).required_scope === 'iam:write')
|
|
21
|
+
return keep(`This credential cannot change projects and workspaces in ${org}: a login needs the organization owner or manager role in IAM, and an API key needs iam:write. Use the Console.`);
|
|
22
|
+
return e;
|
|
23
|
+
}
|