@lifesi/genesis 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/beacons.d.ts +46 -0
- package/dist/beacons.js +20 -0
- package/dist/client.d.ts +7179 -1234
- package/dist/client.js +139 -5
- package/dist/dials.d.ts +254 -6
- package/dist/dials.js +82 -1
- package/dist/fleets.d.ts +53 -1
- package/dist/fleets.js +35 -1
- package/dist/flocks.d.ts +73 -0
- package/dist/flocks.js +22 -0
- package/dist/generated/operations.d.ts +7178 -1233
- package/dist/generated/operations.js +8991 -1579
- package/dist/generated/schema.d.ts +15611 -5129
- package/dist/genesis.d.ts +990 -29
- package/dist/genesis.js +46 -4
- package/dist/genesis.openapi.json +22450 -6816
- package/dist/hives-cells.d.ts +127 -0
- package/dist/hives-cells.js +151 -0
- package/dist/hives-environment.d.ts +28 -0
- package/dist/hives-environment.js +58 -0
- package/dist/hives-functions.d.ts +15 -0
- package/dist/hives-functions.js +94 -0
- package/dist/hives-node.d.ts +9 -0
- package/dist/hives-node.js +152 -0
- package/dist/hives-operations.d.ts +304 -0
- package/dist/hives-operations.js +39 -0
- package/dist/hives-queues.d.ts +121 -0
- package/dist/hives-queues.js +106 -0
- package/dist/hives-resources-transport.d.ts +18 -0
- package/dist/hives-resources-transport.js +165 -0
- package/dist/hives-response.d.ts +2 -0
- package/dist/hives-response.js +32 -0
- package/dist/hives-runtimes.d.ts +34 -0
- package/dist/hives-runtimes.js +32 -0
- package/dist/hives-upload.d.ts +9 -0
- package/dist/hives-upload.js +23 -0
- package/dist/index.d.ts +9 -1
- package/dist/index.js +7 -1
- package/dist/runner-grant.d.ts +10 -0
- package/dist/runner-grant.js +133 -0
- package/dist/runner.d.ts +16 -0
- package/dist/runner.js +4 -0
- package/dist/types.d.ts +11 -1
- package/package.json +14 -2
package/dist/client.js
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { isHivesObjectUpload, hivesObjectBody, hivesContentType, isHivesResourceOperation, resourceError } from './hives-resources-transport.js';
|
|
1
2
|
import { operationTable, services } from './generated/operations.js';
|
|
2
3
|
import { GenesisError } from './errors.js';
|
|
4
|
+
import { validateHivesInvocation, hivesFunctionResponseHeaders, hivesFunctionMetadata } from './hives-functions.js';
|
|
5
|
+
import { validateHivesEnvironment, hivesEnvironmentMetadata, hivesEnvironmentReveal } from './hives-environment.js';
|
|
6
|
+
import { RUNNER_SELF_GRANT_OPERATION, snapshotRunnerSelfGrant, runnerGrantResponse, runnerGrantTransportError } from './runner-grant.js';
|
|
3
7
|
export const DEFAULT_BASE_URL = 'https://console.life.one/api';
|
|
4
8
|
const env = globalThis.process?.env ?? {};
|
|
5
9
|
export class GenesisClient {
|
|
@@ -59,20 +63,71 @@ export class GenesisClient {
|
|
|
59
63
|
/** Call any Genesis operation by id, fully typed from the merged spec. */
|
|
60
64
|
async request(operationId, input, init = {}) {
|
|
61
65
|
const res = await this.raw(operationId, (input ?? {}), init);
|
|
66
|
+
if (operationTable[operationId].rawResponse)
|
|
67
|
+
return res;
|
|
62
68
|
if (res.status === 204)
|
|
63
69
|
return undefined;
|
|
64
70
|
const ct = res.headers.get('content-type') ?? '';
|
|
65
|
-
|
|
71
|
+
let value;
|
|
72
|
+
try {
|
|
73
|
+
value = ct.includes('json') ? await res.json() : await res.text();
|
|
74
|
+
}
|
|
75
|
+
catch (error) {
|
|
76
|
+
if (String(operationId).startsWith('hives_') && String(operationId).endsWith('_project_environment'))
|
|
77
|
+
throw new Error('Hives returned invalid environment data.');
|
|
78
|
+
if (String(operationId).startsWith('hives_') && /_functions?$/.test(operationId))
|
|
79
|
+
throw new Error('Hives returned invalid function metadata.');
|
|
80
|
+
throw error;
|
|
81
|
+
}
|
|
82
|
+
if (operationId === 'hives_list_functions') {
|
|
83
|
+
if (!value || !Array.isArray(value.data) || !value.pagination)
|
|
84
|
+
throw new Error('Hives returned invalid function list metadata.');
|
|
85
|
+
const pagination = Object.fromEntries(['page', 'per_page', 'total', 'total_pages', 'has_next', 'has_prev'].map((key) => [key, value.pagination[key]]));
|
|
86
|
+
return { data: value.data.map((row) => hivesFunctionMetadata(row, input?.project_id)), pagination };
|
|
87
|
+
}
|
|
88
|
+
if (['hives_get_function', 'hives_create_function', 'hives_update_function'].includes(operationId)) {
|
|
89
|
+
return hivesFunctionMetadata(value, input?.project_id);
|
|
90
|
+
}
|
|
91
|
+
if (operationId === 'hives_list_project_environment' || operationId === 'hives_update_project_environment') {
|
|
92
|
+
return hivesEnvironmentMetadata(value, input?.project_id, operationId === 'hives_update_project_environment');
|
|
93
|
+
}
|
|
94
|
+
if (operationId === 'hives_reveal_project_environment') {
|
|
95
|
+
return hivesEnvironmentReveal(value, input?.key);
|
|
96
|
+
}
|
|
97
|
+
return value;
|
|
66
98
|
}
|
|
67
|
-
/**
|
|
99
|
+
/** Raw Response. Completed Hives guest responses retain their actual status, including non-2xx. */
|
|
68
100
|
async raw(operationId, input = {}, init = {}) {
|
|
101
|
+
const runnerGrant = operationId === RUNNER_SELF_GRANT_OPERATION ? snapshotRunnerSelfGrant(input, this.routing) : undefined;
|
|
102
|
+
if (runnerGrant)
|
|
103
|
+
input = { body: runnerGrant };
|
|
69
104
|
const op = operationTable[operationId];
|
|
105
|
+
if (op.rawResponse && op.service === 'hives')
|
|
106
|
+
validateHivesInvocation(input.body);
|
|
107
|
+
const flocksNative = ['flocks_exchangeJoinToken', 'flocks_activateNode', 'flocks_rotateNodeCredential'].includes(operationId);
|
|
108
|
+
if (flocksNative && this.routing === 'direct')
|
|
109
|
+
throw new GenesisError({ status: 0, type: 'invalid_request', message: 'Native Flocks credentials are brokered only through the Genesis gateway.', operationId });
|
|
110
|
+
if (operationId === 'hives_update_project_environment')
|
|
111
|
+
input = { ...input, body: validateHivesEnvironment(input.body) };
|
|
112
|
+
const objectBody = isHivesObjectUpload(operationId) ? await hivesObjectBody(input.body) : undefined;
|
|
70
113
|
const url = this.url(operationId, input);
|
|
71
114
|
const headers = {
|
|
72
115
|
accept: 'application/json',
|
|
73
116
|
'user-agent': this.opts.userAgent ?? '@lifesi/genesis/0.1.0',
|
|
74
117
|
...this.opts.headers, ...init.headers,
|
|
75
118
|
};
|
|
119
|
+
if (runnerGrant) {
|
|
120
|
+
for (const key of Object.keys(headers))
|
|
121
|
+
if (!['accept', 'user-agent'].includes(key.toLowerCase()))
|
|
122
|
+
delete headers[key];
|
|
123
|
+
headers['x-request-id'] = runnerGrant.requestId;
|
|
124
|
+
headers['idempotency-key'] = runnerGrant.operationId;
|
|
125
|
+
}
|
|
126
|
+
if (operationId === 'flocks_exchangeJoinToken') {
|
|
127
|
+
for (const key of Object.keys(headers))
|
|
128
|
+
if (!['accept', 'user-agent'].includes(key.toLowerCase()))
|
|
129
|
+
delete headers[key];
|
|
130
|
+
}
|
|
76
131
|
if (!op.public) {
|
|
77
132
|
const cred = await this.credential();
|
|
78
133
|
// Direct mode: let the upstream decide (some, like Foundry's model list, are public).
|
|
@@ -81,12 +136,91 @@ export class GenesisClient {
|
|
|
81
136
|
if (cred)
|
|
82
137
|
headers.authorization = `Bearer ${cred}`;
|
|
83
138
|
}
|
|
139
|
+
// A timed-out credential provider must not dispatch a later resource mutation.
|
|
140
|
+
if (init.signal?.aborted && isHivesResourceOperation(operationId))
|
|
141
|
+
throw resourceError(0, operationId);
|
|
84
142
|
let body;
|
|
85
143
|
if (op.hasBody && input.body !== undefined) {
|
|
86
|
-
|
|
87
|
-
|
|
144
|
+
// Fetch owns multipart boundary generation. Never serialize a File/Blob as JSON.
|
|
145
|
+
for (const key of Object.keys(headers))
|
|
146
|
+
if (key.toLowerCase() === 'content-type')
|
|
147
|
+
delete headers[key];
|
|
148
|
+
if (objectBody !== undefined && op.requestContentType === 'application/octet-stream') {
|
|
149
|
+
headers['content-type'] = hivesContentType(input.content_type);
|
|
150
|
+
headers['content-length'] = String(objectBody.byteLength);
|
|
151
|
+
body = objectBody;
|
|
152
|
+
}
|
|
153
|
+
else if (op.requestContentType === 'multipart/form-data') {
|
|
154
|
+
if (!(input.body instanceof FormData))
|
|
155
|
+
throw new GenesisError({ status: 0, type: 'invalid_request', operationId,
|
|
156
|
+
message: 'This operation requires a FormData body containing a real binary file.' });
|
|
157
|
+
body = input.body;
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
if (input.body instanceof FormData || input.body instanceof Blob || input.body instanceof ArrayBuffer || ArrayBuffer.isView(input.body)) {
|
|
161
|
+
throw new GenesisError({ status: 0, type: 'invalid_request', operationId, message: 'This operation requires a JSON body.' });
|
|
162
|
+
}
|
|
163
|
+
headers['content-type'] = 'application/json';
|
|
164
|
+
body = JSON.stringify(input.body);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
const environmentOperation = op.service === 'hives' && operationId.endsWith('_project_environment');
|
|
168
|
+
const secretOperation = op.service === 'hives' && (/_secrets?$/.test(operationId) || environmentOperation);
|
|
169
|
+
const functionOperation = op.service === 'hives' && (op.rawResponse || /_function(?:s|_async)?$/.test(operationId));
|
|
170
|
+
let res;
|
|
171
|
+
try {
|
|
172
|
+
res = await this.fetchImpl(url, { method: op.method, headers, body, signal: init.signal,
|
|
173
|
+
...(op.service === 'hives' || op.service === 'flocks' || runnerGrant ? { cache: 'no-store', redirect: op.rawResponse && !runnerGrant ? 'manual' : 'error' } : {}) });
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
if (runnerGrant)
|
|
177
|
+
throw runnerGrantTransportError();
|
|
178
|
+
if (functionOperation)
|
|
179
|
+
throw new GenesisError({ status: 0, type: 'transport_error', operationId,
|
|
180
|
+
message: 'Hives function transport failed. No redirects are followed; check the service address and retry.' });
|
|
181
|
+
if (!secretOperation)
|
|
182
|
+
throw error;
|
|
183
|
+
throw new GenesisError({ status: 0, type: 'transport_error', operationId,
|
|
184
|
+
message: 'Hives secret transport failed. Redirects are refused; check the service address and retry.' });
|
|
185
|
+
}
|
|
186
|
+
if (runnerGrant)
|
|
187
|
+
return runnerGrantResponse(res, runnerGrant);
|
|
188
|
+
if (op.rawResponse && /^exec_[a-f0-9]{32}$/.test(res.headers.get('x-execution-id') ?? '')) {
|
|
189
|
+
return new Response(res.body, { status: res.status, statusText: res.statusText, headers: hivesFunctionResponseHeaders(res.headers) });
|
|
190
|
+
}
|
|
191
|
+
if (op.rawResponse && res.ok)
|
|
192
|
+
throw new GenesisError({ status: res.status, type: 'invalid_response', operationId,
|
|
193
|
+
message: 'Hives did not confirm a completed guest execution. Refresh the resource and retry.' });
|
|
194
|
+
if (functionOperation && !res.ok) {
|
|
195
|
+
const message = res.status === 401 ? 'Genesis credential rejected. Sign in again.'
|
|
196
|
+
: res.status === 403 ? 'Missing the authorized Hives scope for this function action.'
|
|
197
|
+
: res.status === 404 ? 'Function/deployment, project or artifact not found in the authenticated organization and project.'
|
|
198
|
+
: res.status === 409 ? 'Function mutation conflicted. Refresh its metadata before retrying.'
|
|
199
|
+
: res.status === 400 || res.status === 422 ? 'Invalid function request. Check artifact, resource bounds and guest request syntax.'
|
|
200
|
+
: res.status === 413 ? 'Function request exceeds a body or serialized guest envelope limit.'
|
|
201
|
+
: res.status >= 300 && res.status < 400 ? 'Unexpected function service redirect. No redirect was followed.'
|
|
202
|
+
: `Hives function operation failed (${res.status}). Refresh and retry.`;
|
|
203
|
+
throw new GenesisError({ status: res.status, type: `http_${res.status}`, operationId, message,
|
|
204
|
+
requestId: res.headers.get('x-request-id') ?? undefined });
|
|
205
|
+
}
|
|
206
|
+
if (!res.ok && secretOperation) {
|
|
207
|
+
const message = res.status === 401 ? 'Genesis credential rejected. Sign in again.'
|
|
208
|
+
: res.status === 403 ? 'Missing the authorized Hives scope for this action (hives:read or hives:write).'
|
|
209
|
+
: res.status === 404 ? 'Secret or project not found in your authenticated organization.'
|
|
210
|
+
: res.status === 409 ? 'A secret with this name already exists. Update it instead.'
|
|
211
|
+
: res.status === 400 || res.status === 422 ? 'Invalid secret request. Check names and UTF-8 value size.'
|
|
212
|
+
: res.status === 413 ? 'Secret request too large. Use a smaller batch.'
|
|
213
|
+
: res.status === 429 ? 'Too many requests. Wait briefly before retrying.'
|
|
214
|
+
: res.status === 503 ? 'Hives customer encryption is unavailable or stored secrets require protected migration.'
|
|
215
|
+
: `Hives secret operation failed (${res.status}). Refresh metadata and retry.`;
|
|
216
|
+
// Validation/provider bodies may reflect submitted plaintext. Never retain or log them.
|
|
217
|
+
throw new GenesisError({ status: res.status, type: `http_${res.status}`, message, operationId,
|
|
218
|
+
requestId: environmentOperation ? undefined : res.headers.get('x-request-id') ?? undefined });
|
|
219
|
+
}
|
|
220
|
+
if (!res.ok && isHivesResourceOperation(operationId)) {
|
|
221
|
+
await res.body?.cancel().catch(() => { });
|
|
222
|
+
throw resourceError(res.status, operationId);
|
|
88
223
|
}
|
|
89
|
-
const res = await this.fetchImpl(url, { method: op.method, headers, body, signal: init.signal });
|
|
90
224
|
if (!res.ok)
|
|
91
225
|
throw await GenesisError.fromResponse(res, operationId);
|
|
92
226
|
return res;
|
package/dist/dials.d.ts
CHANGED
|
@@ -5,19 +5,236 @@ export type DialsScope = components['schemas']['Dials.Scope'];
|
|
|
5
5
|
export type DialsCallControl = components['schemas']['Dials.CallControl'];
|
|
6
6
|
/** The public call contract requires customer-selected behavior for every caller. */
|
|
7
7
|
export type DialsOutboundCall = components['schemas']['Dials.OutboundCall'];
|
|
8
|
+
export type DialsOutboundCallReceipt = components['schemas']['Dials.OutboundCallReceipt'];
|
|
9
|
+
export type DialsRecoveredOutboundCallReceipt = components['schemas']['Dials.RecoveredOutboundCallReceipt'];
|
|
8
10
|
export type DialsEmergencyAddress = components['schemas']['Dials.RegisterEmergencyAddress'];
|
|
11
|
+
export type DialsCallListItem = components['schemas']['Dials.CallListItem'];
|
|
12
|
+
export type DialsCallDispatchState = components['schemas']['Dials.CallDispatchState'];
|
|
13
|
+
export type DialsMessage = components['schemas']['Dials.MessageEvent'];
|
|
14
|
+
export type DialsMessageTemplate = components['schemas']['Dials.MessageTemplate'];
|
|
15
|
+
export type DialsMessagingBrand = components['schemas']['Dials.MessagingBrand'];
|
|
16
|
+
export type DialsMessagingCampaign = components['schemas']['Dials.MessagingCampaign'];
|
|
17
|
+
export type DialsWorkflowDocument = components['schemas']['Dials.WorkflowDocument'];
|
|
18
|
+
export type DialsRoutingAction = components['schemas']['Dials.RoutingAction'];
|
|
19
|
+
export type DialsRoutingRule = components['schemas']['Dials.RoutingRule'];
|
|
20
|
+
export type DialsRoutingRuleReceipt = components['schemas']['Dials.RoutingRuleReceipt'];
|
|
9
21
|
/** Scope is selection only; the Dials backend verifies its org against signed identity. */
|
|
10
22
|
export declare function dialsModule(client: GenesisClient): {
|
|
23
|
+
routing: {
|
|
24
|
+
list: (scope: DialsScope) => Promise<{
|
|
25
|
+
ok: true;
|
|
26
|
+
scope: components["schemas"]["Dials.Scope"];
|
|
27
|
+
rules: components["schemas"]["Dials.RoutingRule"][];
|
|
28
|
+
}>;
|
|
29
|
+
create: (body: OperationInput<"dials_createRoutingRule">["body"]) => Promise<{
|
|
30
|
+
ok: true;
|
|
31
|
+
rule: components["schemas"]["Dials.RoutingRule"];
|
|
32
|
+
auditId: string;
|
|
33
|
+
idempotentReplay: boolean;
|
|
34
|
+
}>;
|
|
35
|
+
disable: (ruleId: string, body: OperationInput<"dials_disableRoutingRule">["body"]) => Promise<{
|
|
36
|
+
ok: true;
|
|
37
|
+
rule: components["schemas"]["Dials.RoutingRule"];
|
|
38
|
+
auditId: string | null;
|
|
39
|
+
idempotentReplay: boolean;
|
|
40
|
+
}>;
|
|
41
|
+
};
|
|
42
|
+
people: {
|
|
43
|
+
list: (scope: DialsScope) => Promise<{
|
|
44
|
+
ok: boolean;
|
|
45
|
+
humans: components["schemas"]["Dials.HumanProfile"][];
|
|
46
|
+
}>;
|
|
47
|
+
create: (body: OperationInput<"dials_createHumanProfile">["body"]) => Promise<{
|
|
48
|
+
ok: boolean;
|
|
49
|
+
human: components["schemas"]["Dials.HumanProfile"];
|
|
50
|
+
auditId: string;
|
|
51
|
+
usageEventId: string;
|
|
52
|
+
next: string;
|
|
53
|
+
}>;
|
|
54
|
+
};
|
|
55
|
+
agents: {
|
|
56
|
+
list: (scope: DialsScope) => Promise<{
|
|
57
|
+
ok: boolean;
|
|
58
|
+
agents: components["schemas"]["Dials.AgentProfile"][];
|
|
59
|
+
}>;
|
|
60
|
+
create: (body: OperationInput<"dials_createAgent">["body"]) => Promise<{
|
|
61
|
+
ok: boolean;
|
|
62
|
+
agent: components["schemas"]["Dials.AgentProfile"];
|
|
63
|
+
auditId: string;
|
|
64
|
+
usageEventId: string;
|
|
65
|
+
next: string;
|
|
66
|
+
}>;
|
|
67
|
+
};
|
|
68
|
+
voice: {
|
|
69
|
+
list: (scope: DialsScope) => Promise<{
|
|
70
|
+
ok: boolean;
|
|
71
|
+
voiceProfiles: components["schemas"]["Dials.VoiceProfile"][];
|
|
72
|
+
}>;
|
|
73
|
+
create: (body: OperationInput<"dials_createVoiceProfile">["body"]) => Promise<{
|
|
74
|
+
ok: boolean;
|
|
75
|
+
voiceProfile: components["schemas"]["Dials.VoiceProfile"];
|
|
76
|
+
auditId: string;
|
|
77
|
+
usageEventId: string;
|
|
78
|
+
next: string;
|
|
79
|
+
}>;
|
|
80
|
+
};
|
|
81
|
+
messages: {
|
|
82
|
+
list: (scope: DialsScope) => Promise<{
|
|
83
|
+
ok: boolean;
|
|
84
|
+
messages: components["schemas"]["Dials.MessageEvent"][];
|
|
85
|
+
}>;
|
|
86
|
+
send: (body: OperationInput<"dials_createOutboundMessage">["body"]) => Promise<{
|
|
87
|
+
ok: boolean;
|
|
88
|
+
message: components["schemas"]["Dials.MessageEvent"];
|
|
89
|
+
auditId: string;
|
|
90
|
+
usageEventId: string;
|
|
91
|
+
next: string;
|
|
92
|
+
idempotentReplay: boolean;
|
|
93
|
+
}>;
|
|
94
|
+
templates: {
|
|
95
|
+
list: (scope: DialsScope) => Promise<{
|
|
96
|
+
ok: boolean;
|
|
97
|
+
templates: components["schemas"]["Dials.MessageTemplate"][];
|
|
98
|
+
}>;
|
|
99
|
+
create: (body: OperationInput<"dials_createMessageTemplate">["body"]) => Promise<{
|
|
100
|
+
ok: boolean;
|
|
101
|
+
template: components["schemas"]["Dials.MessageTemplate"];
|
|
102
|
+
auditId: string;
|
|
103
|
+
usageEventId: string;
|
|
104
|
+
next: string;
|
|
105
|
+
}>;
|
|
106
|
+
};
|
|
107
|
+
brands: {
|
|
108
|
+
list: (scope: DialsScope) => Promise<{
|
|
109
|
+
ok: boolean;
|
|
110
|
+
brands: components["schemas"]["Dials.MessagingBrand"][];
|
|
111
|
+
}>;
|
|
112
|
+
create: (body: OperationInput<"dials_createMessagingBrand">["body"]) => Promise<{
|
|
113
|
+
ok: boolean;
|
|
114
|
+
brand: components["schemas"]["Dials.MessagingBrand"];
|
|
115
|
+
auditId: string;
|
|
116
|
+
next: string;
|
|
117
|
+
}>;
|
|
118
|
+
};
|
|
119
|
+
campaigns: {
|
|
120
|
+
list: (scope: DialsScope) => Promise<{
|
|
121
|
+
ok: boolean;
|
|
122
|
+
campaigns: components["schemas"]["Dials.MessagingCampaign"][];
|
|
123
|
+
}>;
|
|
124
|
+
create: (body: OperationInput<"dials_createMessagingCampaign">["body"]) => Promise<{
|
|
125
|
+
ok: boolean;
|
|
126
|
+
campaign: components["schemas"]["Dials.MessagingCampaign"];
|
|
127
|
+
auditId: string;
|
|
128
|
+
next: string;
|
|
129
|
+
}>;
|
|
130
|
+
assignNumber: (campaignId: string, body: OperationInput<"dials_assignCampaignNumber">["body"]) => Promise<{
|
|
131
|
+
ok: boolean;
|
|
132
|
+
campaignId: string;
|
|
133
|
+
phoneNumberId: string;
|
|
134
|
+
auditId: string;
|
|
135
|
+
next: string;
|
|
136
|
+
}>;
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
workflows: {
|
|
140
|
+
list: (scope: DialsScope) => Promise<{
|
|
141
|
+
ok: boolean;
|
|
142
|
+
workflows: components["schemas"]["Dials.WorkflowDocument"][];
|
|
143
|
+
}>;
|
|
144
|
+
deploy: (body: OperationInput<"dials_deployWorkflow">["body"]) => Promise<{
|
|
145
|
+
ok: boolean;
|
|
146
|
+
workflow: components["schemas"]["Dials.WorkflowDocument"];
|
|
147
|
+
auditId: string;
|
|
148
|
+
usageEventId: string;
|
|
149
|
+
next: string;
|
|
150
|
+
route: components["schemas"]["Dials.RoutingRule"];
|
|
151
|
+
}>;
|
|
152
|
+
};
|
|
153
|
+
overview: {
|
|
154
|
+
summary: (scope: DialsScope) => Promise<{
|
|
155
|
+
ok: boolean;
|
|
156
|
+
scope: components["schemas"]["Dials.Scope"];
|
|
157
|
+
calls: {
|
|
158
|
+
total: number;
|
|
159
|
+
queued: number;
|
|
160
|
+
active: number;
|
|
161
|
+
completed: number;
|
|
162
|
+
failed: number;
|
|
163
|
+
};
|
|
164
|
+
dispatch: {
|
|
165
|
+
total: number;
|
|
166
|
+
pending: number;
|
|
167
|
+
claimed: number;
|
|
168
|
+
dispatched: number;
|
|
169
|
+
failed: number;
|
|
170
|
+
};
|
|
171
|
+
people: {
|
|
172
|
+
deployed: number;
|
|
173
|
+
};
|
|
174
|
+
agents: {
|
|
175
|
+
deployed: number;
|
|
176
|
+
};
|
|
177
|
+
routes: {
|
|
178
|
+
deployed: number;
|
|
179
|
+
};
|
|
180
|
+
numbers: {
|
|
181
|
+
deployed: number;
|
|
182
|
+
};
|
|
183
|
+
voice: {
|
|
184
|
+
deployed: number;
|
|
185
|
+
};
|
|
186
|
+
messages: {
|
|
187
|
+
total: number;
|
|
188
|
+
queued: number;
|
|
189
|
+
delivered: number;
|
|
190
|
+
failed: number;
|
|
191
|
+
};
|
|
192
|
+
usage: {
|
|
193
|
+
total: number;
|
|
194
|
+
quantity: number;
|
|
195
|
+
lastMetric: string | null;
|
|
196
|
+
lastResourceId: string | null;
|
|
197
|
+
};
|
|
198
|
+
}>;
|
|
199
|
+
operatingSurface: (scope: DialsScope) => Promise<{
|
|
200
|
+
ok: boolean;
|
|
201
|
+
scope: components["schemas"]["Dials.Scope"];
|
|
202
|
+
summary: components["schemas"]["Dials.ConsoleSummary"];
|
|
203
|
+
surfaces: components["schemas"]["Dials.OperatingSurfaceCard"][];
|
|
204
|
+
} & {
|
|
205
|
+
[key: string]: unknown;
|
|
206
|
+
}>;
|
|
207
|
+
};
|
|
11
208
|
numbers: {
|
|
209
|
+
reconciliations: (scope: DialsScope) => Promise<{
|
|
210
|
+
ok: boolean;
|
|
211
|
+
reconciliations: components["schemas"]["Dials.InventoryReconciliation"][];
|
|
212
|
+
}>;
|
|
213
|
+
drift: (scope: DialsScope) => Promise<{
|
|
214
|
+
ok: boolean;
|
|
215
|
+
drift: components["schemas"]["Dials.InventoryDrift"][];
|
|
216
|
+
}>;
|
|
217
|
+
updateCallerName: (body: OperationInput<"dials_updateNumberCallerName">["body"]) => Promise<{
|
|
218
|
+
ok: boolean;
|
|
219
|
+
number: components["schemas"]["Dials.Number"];
|
|
220
|
+
auditId: string;
|
|
221
|
+
next: string;
|
|
222
|
+
}>;
|
|
223
|
+
reconcile: (body: OperationInput<"dials_reconcileNumberInventory">["body"]) => Promise<{
|
|
224
|
+
ok: boolean;
|
|
225
|
+
reconciliations: components["schemas"]["Dials.InventoryReconciliation"][];
|
|
226
|
+
drift: components["schemas"]["Dials.InventoryDrift"][];
|
|
227
|
+
orphanCount: number;
|
|
228
|
+
auditId: string;
|
|
229
|
+
next: string;
|
|
230
|
+
}>;
|
|
12
231
|
list: (scope: DialsScope) => Promise<{
|
|
13
232
|
ok: boolean;
|
|
14
233
|
numbers: components["schemas"]["Dials.Number"][];
|
|
15
234
|
}>;
|
|
16
|
-
search: (scope: DialsScope, query?: {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}) => Promise<{
|
|
20
|
-
[key: string]: unknown;
|
|
235
|
+
search: (scope: DialsScope, query?: Omit<OperationInput<"dials_searchNumbers">, keyof DialsScope>) => Promise<{
|
|
236
|
+
ok: boolean;
|
|
237
|
+
numbers: components["schemas"]["Dials.AvailableNumber"][];
|
|
21
238
|
}>;
|
|
22
239
|
provision: (body: OperationInput<"dials_provisionNumber">["body"]) => Promise<{
|
|
23
240
|
[key: string]: unknown;
|
|
@@ -25,6 +242,13 @@ export declare function dialsModule(client: GenesisClient): {
|
|
|
25
242
|
assign: (body: OperationInput<"dials_assignNumber">["body"]) => Promise<{
|
|
26
243
|
[key: string]: unknown;
|
|
27
244
|
}>;
|
|
245
|
+
delete: (input: OperationInput<"dials_deleteNumber">) => Promise<{
|
|
246
|
+
ok: boolean;
|
|
247
|
+
number: components["schemas"]["Dials.Number"];
|
|
248
|
+
auditId: string;
|
|
249
|
+
usageEventId: string;
|
|
250
|
+
next: string;
|
|
251
|
+
}>;
|
|
28
252
|
release: (body: OperationInput<"dials_releaseNumber">["body"]) => Promise<{
|
|
29
253
|
[key: string]: unknown;
|
|
30
254
|
}>;
|
|
@@ -39,13 +263,37 @@ export declare function dialsModule(client: GenesisClient): {
|
|
|
39
263
|
calls: {
|
|
40
264
|
list: (scope: DialsScope) => Promise<{
|
|
41
265
|
ok: boolean;
|
|
42
|
-
calls: components["schemas"]["Dials.
|
|
266
|
+
calls: components["schemas"]["Dials.CallListItem"][];
|
|
43
267
|
}>;
|
|
44
268
|
validate: (body: DialsOutboundCall) => Promise<{
|
|
45
269
|
[key: string]: unknown;
|
|
46
270
|
}>;
|
|
47
271
|
create: (body: DialsOutboundCall) => Promise<{
|
|
272
|
+
ok: boolean;
|
|
273
|
+
call: components["schemas"]["Dials.CallSession"];
|
|
274
|
+
auditId: string;
|
|
275
|
+
dispatchJobId: string;
|
|
276
|
+
usageEventId: string;
|
|
277
|
+
next: string;
|
|
278
|
+
idempotentReplay: boolean;
|
|
279
|
+
} & {
|
|
280
|
+
[key: string]: unknown;
|
|
281
|
+
}>;
|
|
282
|
+
recover: (body: DialsOutboundCall) => Promise<{
|
|
283
|
+
ok: boolean;
|
|
284
|
+
call: components["schemas"]["Dials.CallSession"];
|
|
285
|
+
auditId: string;
|
|
286
|
+
dispatchJobId: string;
|
|
287
|
+
usageEventId: string;
|
|
288
|
+
next: string;
|
|
289
|
+
idempotentReplay: boolean;
|
|
290
|
+
} & {
|
|
48
291
|
[key: string]: unknown;
|
|
292
|
+
} & {
|
|
293
|
+
idempotentReplay: true;
|
|
49
294
|
}>;
|
|
50
295
|
};
|
|
51
296
|
};
|
|
297
|
+
export type DialsEmergencyAddressFeedback = components['schemas']['Dials.EmergencyAddressFeedback'];
|
|
298
|
+
/** Read bounded, typed advisory feedback from the existing Genesis error. Never auto-apply it. */
|
|
299
|
+
export declare function dialsEmergencyAddressFeedback(error: unknown): DialsEmergencyAddressFeedback | undefined;
|
package/dist/dials.js
CHANGED
|
@@ -1,11 +1,59 @@
|
|
|
1
|
+
import { GenesisError } from './errors.js';
|
|
1
2
|
/** Scope is selection only; the Dials backend verifies its org against signed identity. */
|
|
2
3
|
export function dialsModule(client) {
|
|
3
4
|
return {
|
|
5
|
+
routing: {
|
|
6
|
+
list: (scope) => client.request('dials_listRoutingRules', scope),
|
|
7
|
+
create: (body) => client.request('dials_createRoutingRule', { body }),
|
|
8
|
+
disable: (ruleId, body) => client.request('dials_disableRoutingRule', { rule_id: ruleId, body }),
|
|
9
|
+
},
|
|
10
|
+
people: {
|
|
11
|
+
list: (scope) => client.request('dials_listHumanProfiles', scope),
|
|
12
|
+
create: (body) => client.request('dials_createHumanProfile', { body }),
|
|
13
|
+
},
|
|
14
|
+
agents: {
|
|
15
|
+
list: (scope) => client.request('dials_listAgents', scope),
|
|
16
|
+
create: (body) => client.request('dials_createAgent', { body }),
|
|
17
|
+
},
|
|
18
|
+
voice: {
|
|
19
|
+
list: (scope) => client.request('dials_listVoiceProfiles', scope),
|
|
20
|
+
create: (body) => client.request('dials_createVoiceProfile', { body }),
|
|
21
|
+
},
|
|
22
|
+
messages: {
|
|
23
|
+
list: (scope) => client.request('dials_listMessages', scope),
|
|
24
|
+
send: (body) => client.request('dials_createOutboundMessage', { body }),
|
|
25
|
+
templates: {
|
|
26
|
+
list: (scope) => client.request('dials_listMessageTemplates', scope),
|
|
27
|
+
create: (body) => client.request('dials_createMessageTemplate', { body }),
|
|
28
|
+
},
|
|
29
|
+
brands: {
|
|
30
|
+
list: (scope) => client.request('dials_listMessagingBrands', scope),
|
|
31
|
+
create: (body) => client.request('dials_createMessagingBrand', { body }),
|
|
32
|
+
},
|
|
33
|
+
campaigns: {
|
|
34
|
+
list: (scope) => client.request('dials_listMessagingCampaigns', scope),
|
|
35
|
+
create: (body) => client.request('dials_createMessagingCampaign', { body }),
|
|
36
|
+
assignNumber: (campaignId, body) => client.request('dials_assignCampaignNumber', { campaign_id: campaignId, body }),
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
workflows: {
|
|
40
|
+
list: (scope) => client.request('dials_listWorkflows', scope),
|
|
41
|
+
deploy: (body) => client.request('dials_deployWorkflow', { body }),
|
|
42
|
+
},
|
|
43
|
+
overview: {
|
|
44
|
+
summary: (scope) => client.request('dials_getConsoleSummary', scope),
|
|
45
|
+
operatingSurface: (scope) => client.request('dials_getOperatingSurface', scope),
|
|
46
|
+
},
|
|
4
47
|
numbers: {
|
|
48
|
+
reconciliations: (scope) => client.request('dials_listNumberReconciliations', scope),
|
|
49
|
+
drift: (scope) => client.request('dials_listNumberDrift', scope),
|
|
50
|
+
updateCallerName: (body) => client.request('dials_updateNumberCallerName', { body }),
|
|
51
|
+
reconcile: (body) => client.request('dials_reconcileNumberInventory', { body }),
|
|
5
52
|
list: (scope) => client.request('dials_listNumbers', scope),
|
|
6
|
-
search: (scope, query = {}) => client.request('dials_searchNumbers', { ...
|
|
53
|
+
search: (scope, query = {}) => client.request('dials_searchNumbers', { ...query, ...scope }),
|
|
7
54
|
provision: (body) => client.request('dials_provisionNumber', { body }),
|
|
8
55
|
assign: (body) => client.request('dials_assignNumber', { body }),
|
|
56
|
+
delete: (input) => client.request('dials_deleteNumber', input),
|
|
9
57
|
release: (body) => client.request('dials_releaseNumber', { body }),
|
|
10
58
|
registerEmergencyAddress: (body) => client.request('dials_registerEmergencyAddress', { body }),
|
|
11
59
|
},
|
|
@@ -13,6 +61,39 @@ export function dialsModule(client) {
|
|
|
13
61
|
list: (scope) => client.request('dials_listCalls', scope),
|
|
14
62
|
validate: (body) => client.request('dials_validateOutboundCall', { body }),
|
|
15
63
|
create: (body) => client.request('dials_createOutboundCall', { body }),
|
|
64
|
+
recover: (body) => client.request('dials_recoverOutboundCall', { body }),
|
|
16
65
|
},
|
|
17
66
|
};
|
|
18
67
|
}
|
|
68
|
+
const fields = { streetNumber: 'Review the street number.', streetName: 'Review the street name.', addressLine2: 'Review the suite or unit type and number.', city: 'Review the city.', region: 'Review the state.', postalCode: 'Review the postal code.', country: 'Review the country.', address: 'The carrier could not validate this address. Review the address and any suggestions.' };
|
|
69
|
+
const object = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
70
|
+
const addressText = (value, max) => typeof value === 'string' && value.length > 0 && value.length <= max && /^[\p{L}\p{N} .,'#/-]+$/u.test(value) && !/(bearer|basic |sk_|api_key|authorization|secret|token=)/i.test(value);
|
|
71
|
+
/** Read bounded, typed advisory feedback from the existing Genesis error. Never auto-apply it. */
|
|
72
|
+
export function dialsEmergencyAddressFeedback(error) {
|
|
73
|
+
if (!(error instanceof GenesisError) || error.status !== 422 || error.operationId !== 'dials_registerEmergencyAddress')
|
|
74
|
+
return;
|
|
75
|
+
const body = error.body;
|
|
76
|
+
if (!object(body) || body.error !== 'carrier_rejected' || body.automaticCorrection !== false || !Array.isArray(body.fieldErrors) || !body.fieldErrors.length || body.fieldErrors.length > 8 || !Array.isArray(body.candidates) || body.candidates.length > 5)
|
|
77
|
+
return;
|
|
78
|
+
const fieldErrors = [];
|
|
79
|
+
for (const value of body.fieldErrors) {
|
|
80
|
+
if (!object(value) || typeof value.field !== 'string' || !Object.hasOwn(fields, value.field) || !['required', 'invalid', 'not_found', 'ambiguous', 'not_validated'].includes(String(value.code)))
|
|
81
|
+
return;
|
|
82
|
+
const field = value.field;
|
|
83
|
+
fieldErrors.push({ field, code: value.code, message: fields[field] });
|
|
84
|
+
}
|
|
85
|
+
const candidates = [];
|
|
86
|
+
for (const value of body.candidates) {
|
|
87
|
+
if (!object(value) || !addressText(value.streetNumber, 32) || !addressText(value.streetName, 128) || !addressText(value.city, 80) || typeof value.region !== 'string' || !/^[A-Za-z]{2}$/.test(value.region) || typeof value.postalCode !== 'string' || !/^\d{5}(?:-\d{4})?$/.test(value.postalCode))
|
|
88
|
+
return;
|
|
89
|
+
const extra = {};
|
|
90
|
+
if (value.addressType !== undefined || value.addressNumber !== undefined) {
|
|
91
|
+
if (!['Apartment', 'Basement', 'Building', 'Department', 'Floor', 'Office', 'Penthouse', 'Suite', 'Trailer', 'Unit'].includes(String(value.addressType)) || !addressText(value.addressNumber, 32))
|
|
92
|
+
return;
|
|
93
|
+
extra.addressType = value.addressType;
|
|
94
|
+
extra.addressNumber = value.addressNumber;
|
|
95
|
+
}
|
|
96
|
+
candidates.push({ streetNumber: value.streetNumber, streetName: value.streetName, city: value.city, region: value.region, postalCode: value.postalCode, ...extra });
|
|
97
|
+
}
|
|
98
|
+
return { automaticCorrection: false, fieldErrors, candidates };
|
|
99
|
+
}
|
package/dist/fleets.d.ts
CHANGED
|
@@ -1,8 +1,60 @@
|
|
|
1
1
|
import type { GenesisClient } from './client.js';
|
|
2
2
|
import type { OperationInput } from './types.js';
|
|
3
3
|
type TaskInput = NonNullable<OperationInput<'fleets_submitTask'>['body']>;
|
|
4
|
-
|
|
4
|
+
export interface FleetsInstanceOptions {
|
|
5
|
+
idempotencyKey?: string;
|
|
6
|
+
signal?: AbortSignal;
|
|
7
|
+
}
|
|
8
|
+
/** Managed Henry instances and tasks scoped to the credential's verified org. */
|
|
5
9
|
export declare function fleetsModule(client: GenesisClient): {
|
|
10
|
+
instances: {
|
|
11
|
+
list: (query?: OperationInput<"fleets_listInstances">) => Promise<{
|
|
12
|
+
instances: import("./index.js").components["schemas"]["Fleets.Instance"][];
|
|
13
|
+
next_cursor: string | null;
|
|
14
|
+
}>;
|
|
15
|
+
get: (instance_id: string) => Promise<{
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
organization_id: string;
|
|
19
|
+
workspace_id: string;
|
|
20
|
+
project_id: string;
|
|
21
|
+
admission_held: boolean | null;
|
|
22
|
+
physical_state: "created" | "running" | "held" | "stopped" | "recovery_required" | "released";
|
|
23
|
+
status_evidence: "live_controller" | "confirmed_release_receipt";
|
|
24
|
+
release_acknowledged_at_ms: number | null;
|
|
25
|
+
harness_ready: boolean;
|
|
26
|
+
observed_at_ms: number | null;
|
|
27
|
+
lease_expires: {
|
|
28
|
+
wall_ms: number;
|
|
29
|
+
logical: number;
|
|
30
|
+
} | null;
|
|
31
|
+
}>;
|
|
32
|
+
stop: (instance_id: string, options?: FleetsInstanceOptions) => Promise<{
|
|
33
|
+
id: string;
|
|
34
|
+
state: "released";
|
|
35
|
+
reaped: true;
|
|
36
|
+
data_retained: true;
|
|
37
|
+
status_evidence: "confirmed_release_receipt";
|
|
38
|
+
release_acknowledged_at_ms: number;
|
|
39
|
+
}>;
|
|
40
|
+
start: (instance_id: string, options?: FleetsInstanceOptions) => Promise<{
|
|
41
|
+
id: string;
|
|
42
|
+
name: string;
|
|
43
|
+
organization_id: string;
|
|
44
|
+
workspace_id: string;
|
|
45
|
+
project_id: string;
|
|
46
|
+
admission_held: boolean | null;
|
|
47
|
+
physical_state: "created" | "running" | "held" | "stopped" | "recovery_required" | "released";
|
|
48
|
+
status_evidence: "live_controller" | "confirmed_release_receipt";
|
|
49
|
+
release_acknowledged_at_ms: number | null;
|
|
50
|
+
harness_ready: boolean;
|
|
51
|
+
observed_at_ms: number | null;
|
|
52
|
+
lease_expires: {
|
|
53
|
+
wall_ms: number;
|
|
54
|
+
logical: number;
|
|
55
|
+
} | null;
|
|
56
|
+
}>;
|
|
57
|
+
};
|
|
6
58
|
connection: {
|
|
7
59
|
get: () => Promise<{
|
|
8
60
|
connected: boolean;
|