@the-open-engine/zeroshot 6.35.2 → 6.36.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/lib/agent-cli-provider/adapters/omp.js +1 -1
- package/lib/agent-cli-provider/adapters/omp.js.map +1 -1
- package/lib/agent-cli-provider/single-agent-runtime.js +1 -1
- package/lib/agent-cli-provider/single-agent-runtime.js.map +1 -1
- package/lib/hosted-target/adapter-types.d.cts +0 -8
- package/lib/hosted-target/adapter-types.d.mts +0 -8
- package/lib/hosted-target/adapter-types.d.ts +0 -8
- package/lib/hosted-target/index.d.cts +1 -1
- package/lib/hosted-target/index.d.mts +1 -1
- package/lib/hosted-target/index.d.ts +1 -1
- package/lib/hosted-target/retry-executor.d.cts +1 -1
- package/lib/hosted-target/retry-executor.d.mts +1 -1
- package/lib/hosted-target/retry-executor.d.ts +1 -1
- package/lib/hosted-target/target-adapter.d.cts +1 -1
- package/lib/hosted-target/target-adapter.d.mts +1 -1
- package/lib/hosted-target/target-adapter.d.ts +1 -1
- package/lib/hosted-target/zero-cloud-v1-adapter.cjs +0 -29
- package/lib/hosted-target/zero-cloud-v1-adapter.d.cts +0 -2
- package/lib/hosted-target/zero-cloud-v1-adapter.d.mts +0 -2
- package/lib/hosted-target/zero-cloud-v1-adapter.d.ts +0 -2
- package/lib/hosted-target/zero-cloud-v1-adapter.mjs +0 -29
- package/lib/target/discovery-sections.cjs +2 -21
- package/lib/target/discovery-sections.d.cts +0 -2
- package/lib/target/discovery-sections.d.mts +0 -2
- package/lib/target/discovery-sections.d.ts +0 -2
- package/lib/target/discovery-sections.js +2 -21
- package/lib/target/discovery-sections.mjs +3 -22
- package/lib/target/discovery-validation.cjs +4 -28
- package/lib/target/discovery-validation.d.cts +0 -10
- package/lib/target/discovery-validation.d.mts +0 -10
- package/lib/target/discovery-validation.d.ts +0 -10
- package/lib/target/discovery-validation.js +4 -28
- package/lib/target/discovery-validation.mjs +4 -27
- package/lib/target/discovery.cjs +0 -1
- package/lib/target/discovery.d.cts +0 -3
- package/lib/target/discovery.d.mts +0 -3
- package/lib/target/discovery.d.ts +0 -3
- package/lib/target/discovery.js +0 -1
- package/lib/target/discovery.mjs +1 -2
- package/lib/target/index.d.ts +1 -1
- package/lib/target/run-intent-discovery.cjs +3 -1
- package/lib/target/run-intent-discovery.js +3 -1
- package/lib/target/run-intent-discovery.mjs +4 -2
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/src/agent-cli-provider/adapters/omp.ts +1 -1
- package/src/agent-cli-provider/single-agent-runtime.ts +1 -5
- package/src/hosted-target/adapter-types.ts +0 -14
- package/src/hosted-target/index.ts +0 -1
- package/src/hosted-target/retry-executor.ts +12 -14
- package/src/hosted-target/target-adapter.ts +1 -5
- package/src/hosted-target/zero-cloud-v1-adapter.ts +0 -37
- package/src/target/discovery-sections.ts +3 -28
- package/src/target/discovery-validation.ts +15 -61
- package/src/target/discovery.ts +1 -9
- package/src/target/index.ts +0 -1
- package/src/target/run-intent-discovery.ts +3 -2
- package/lib/hosted-target/runtime-install.cjs +0 -45
- package/lib/hosted-target/runtime-install.d.cts +0 -14
- package/lib/hosted-target/runtime-install.d.mts +0 -14
- package/lib/hosted-target/runtime-install.d.ts +0 -14
- package/lib/hosted-target/runtime-install.mjs +0 -42
- package/src/hosted-target/runtime-install.ts +0 -83
|
@@ -1,11 +1,4 @@
|
|
|
1
1
|
import { TargetDiscoveryError } from './discovery-errors.js';
|
|
2
|
-
import { routeTemplate, type RouteTemplate } from './route-template.js';
|
|
3
|
-
|
|
4
|
-
export interface CredentialInstallDescriptor {
|
|
5
|
-
readonly kind: 'openengine.capsule-credential-install/v1';
|
|
6
|
-
readonly install: { readonly routeTemplate: RouteTemplate; readonly method: 'PUT' };
|
|
7
|
-
readonly maxBodyBytes: number;
|
|
8
|
-
}
|
|
9
2
|
|
|
10
3
|
export function record(value: unknown, field: string): Record<string, unknown> {
|
|
11
4
|
if (value === null || typeof value !== 'object' || Array.isArray(value)) {
|
|
@@ -17,7 +10,7 @@ export function record(value: unknown, field: string): Record<string, unknown> {
|
|
|
17
10
|
export function closedRecord(
|
|
18
11
|
value: unknown,
|
|
19
12
|
field: string,
|
|
20
|
-
fields: readonly string[]
|
|
13
|
+
fields: readonly string[]
|
|
21
14
|
): Record<string, unknown> {
|
|
22
15
|
const result = record(value, field);
|
|
23
16
|
const unknown = Object.keys(result).find((key) => !fields.includes(key));
|
|
@@ -25,11 +18,7 @@ export function closedRecord(
|
|
|
25
18
|
return result;
|
|
26
19
|
}
|
|
27
20
|
|
|
28
|
-
export function stringField(
|
|
29
|
-
source: Record<string, unknown>,
|
|
30
|
-
field: string,
|
|
31
|
-
parent = '',
|
|
32
|
-
): string {
|
|
21
|
+
export function stringField(source: Record<string, unknown>, field: string, parent = ''): string {
|
|
33
22
|
const value = source[field];
|
|
34
23
|
if (typeof value !== 'string' || value.length === 0) {
|
|
35
24
|
throw new TargetDiscoveryError(`${parent}${field} must be a non-empty string`);
|
|
@@ -45,7 +34,7 @@ export function integer(
|
|
|
45
34
|
value: unknown,
|
|
46
35
|
field: string,
|
|
47
36
|
min: number,
|
|
48
|
-
max = Number.MAX_SAFE_INTEGER
|
|
37
|
+
max = Number.MAX_SAFE_INTEGER
|
|
49
38
|
): number {
|
|
50
39
|
if (!Number.isSafeInteger(value) || (value as number) < min || (value as number) > max) {
|
|
51
40
|
throw new TargetDiscoveryError(`${field} is outside the supported bounds`);
|
|
@@ -56,36 +45,37 @@ export function integer(
|
|
|
56
45
|
export function exactStringSet(
|
|
57
46
|
value: unknown,
|
|
58
47
|
field: string,
|
|
59
|
-
expected: readonly string[]
|
|
48
|
+
expected: readonly string[]
|
|
60
49
|
): readonly string[] {
|
|
61
50
|
if (!Array.isArray(value) || value.some((item) => typeof item !== 'string')) {
|
|
62
51
|
throw new TargetDiscoveryError(`${field} must be an array of strings`);
|
|
63
52
|
}
|
|
64
|
-
if (
|
|
65
|
-
|
|
53
|
+
if (
|
|
54
|
+
value.length !== expected.length ||
|
|
55
|
+
new Set(value).size !== value.length ||
|
|
56
|
+
expected.some((item) => !value.includes(item))
|
|
57
|
+
) {
|
|
66
58
|
throw new TargetDiscoveryError(`${field} does not match the supported contract`);
|
|
67
59
|
}
|
|
68
60
|
return Object.freeze([...value]) as readonly string[];
|
|
69
61
|
}
|
|
70
62
|
|
|
71
|
-
function hasForbiddenUrlComponent(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
protocols: readonly string[],
|
|
75
|
-
): boolean {
|
|
76
|
-
return !protocols.includes(url.protocol) ||
|
|
63
|
+
function hasForbiddenUrlComponent(url: URL, value: string, protocols: readonly string[]): boolean {
|
|
64
|
+
return (
|
|
65
|
+
!protocols.includes(url.protocol) ||
|
|
77
66
|
url.username !== '' ||
|
|
78
67
|
url.password !== '' ||
|
|
79
68
|
url.search !== '' ||
|
|
80
69
|
url.hash !== '' ||
|
|
81
|
-
url.href !== value
|
|
70
|
+
url.href !== value
|
|
71
|
+
);
|
|
82
72
|
}
|
|
83
73
|
|
|
84
74
|
export function sameOriginUrl(
|
|
85
75
|
value: unknown,
|
|
86
76
|
field: string,
|
|
87
77
|
origin: string,
|
|
88
|
-
protocols: readonly string[] = ['https:', 'http:']
|
|
78
|
+
protocols: readonly string[] = ['https:', 'http:']
|
|
89
79
|
): string {
|
|
90
80
|
if (typeof value !== 'string' || /[\u0000-\u0020\u007f]|\s/u.test(value)) {
|
|
91
81
|
throw new TargetDiscoveryError(`${field} must be a canonical absolute URL`);
|
|
@@ -104,39 +94,3 @@ export function sameOriginUrl(
|
|
|
104
94
|
}
|
|
105
95
|
return url.href;
|
|
106
96
|
}
|
|
107
|
-
|
|
108
|
-
export function parseCredentialInstall(value: unknown): CredentialInstallDescriptor | null {
|
|
109
|
-
if (value === undefined || value === null) return null;
|
|
110
|
-
const extension = closedRecord(value, 'extensions.credential_install', [
|
|
111
|
-
'kind',
|
|
112
|
-
'install',
|
|
113
|
-
'max_body_bytes',
|
|
114
|
-
]);
|
|
115
|
-
exact(
|
|
116
|
-
extension.kind,
|
|
117
|
-
'openengine.capsule-credential-install/v1',
|
|
118
|
-
'extensions.credential_install.kind'
|
|
119
|
-
);
|
|
120
|
-
const install = closedRecord(extension.install, 'extensions.credential_install.install', [
|
|
121
|
-
'route_template',
|
|
122
|
-
'method',
|
|
123
|
-
]);
|
|
124
|
-
exact(install.method, 'PUT', 'extensions.credential_install.install.method');
|
|
125
|
-
return Object.freeze({
|
|
126
|
-
kind: 'openengine.capsule-credential-install/v1' as const,
|
|
127
|
-
install: Object.freeze({
|
|
128
|
-
routeTemplate: routeTemplate(
|
|
129
|
-
install.route_template,
|
|
130
|
-
'extensions.credential_install.install.route_template',
|
|
131
|
-
['capsule_id']
|
|
132
|
-
),
|
|
133
|
-
method: 'PUT' as const,
|
|
134
|
-
}),
|
|
135
|
-
maxBodyBytes: integer(
|
|
136
|
-
extension.max_body_bytes,
|
|
137
|
-
'extensions.credential_install.max_body_bytes',
|
|
138
|
-
1,
|
|
139
|
-
4 * 1024 * 1024
|
|
140
|
-
),
|
|
141
|
-
});
|
|
142
|
-
}
|
package/src/target/discovery.ts
CHANGED
|
@@ -2,12 +2,7 @@ import type { HttpTransport } from './device-flow.js';
|
|
|
2
2
|
import { readBoundedResponseJson } from './bounded-response.js';
|
|
3
3
|
import { TargetDiscoveryError } from './discovery-errors.js';
|
|
4
4
|
import type { RouteTemplate } from './route-template.js';
|
|
5
|
-
import {
|
|
6
|
-
exact,
|
|
7
|
-
exactStringSet,
|
|
8
|
-
record,
|
|
9
|
-
type CredentialInstallDescriptor,
|
|
10
|
-
} from './discovery-validation.js';
|
|
5
|
+
import { exact, exactStringSet, record } from './discovery-validation.js';
|
|
11
6
|
import type { RunIntentDescriptor } from './run-intent-discovery.js';
|
|
12
7
|
import {
|
|
13
8
|
parseAdapter,
|
|
@@ -20,7 +15,6 @@ import {
|
|
|
20
15
|
parseTransport,
|
|
21
16
|
validateOAuthMetadata,
|
|
22
17
|
} from './discovery-sections.js';
|
|
23
|
-
export type { CredentialInstallDescriptor } from './discovery-validation.js';
|
|
24
18
|
export type { RunIntentDescriptor } from './run-intent-discovery.js';
|
|
25
19
|
export { TargetDiscoveryError } from './discovery-errors.js';
|
|
26
20
|
export { expandRoute, type RouteTemplate } from './route-template.js';
|
|
@@ -85,7 +79,6 @@ export interface TargetDiscoveryDescriptor {
|
|
|
85
79
|
readonly closeCodes: { readonly expired: 4401; readonly revoked: 4403 };
|
|
86
80
|
};
|
|
87
81
|
readonly capabilityFlags: readonly string[];
|
|
88
|
-
readonly credentialInstall: CredentialInstallDescriptor | null;
|
|
89
82
|
readonly runIntent: RunIntentDescriptor | null;
|
|
90
83
|
readonly additional: Readonly<Record<string, unknown>>;
|
|
91
84
|
}
|
|
@@ -147,7 +140,6 @@ function parseDiscoveryDocument(
|
|
|
147
140
|
capsule,
|
|
148
141
|
transport,
|
|
149
142
|
capabilityFlags,
|
|
150
|
-
credentialInstall: extensions.credentialInstall,
|
|
151
143
|
runIntent: extensions.runIntent,
|
|
152
144
|
additional,
|
|
153
145
|
});
|
package/src/target/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { RouteTemplate } from './route-template.js';
|
|
2
2
|
import { routeTemplate } from './route-template.js';
|
|
3
|
-
import { closedRecord,
|
|
3
|
+
import { closedRecord, record, sameOriginUrl } from './discovery-validation.js';
|
|
4
4
|
|
|
5
5
|
export interface RunIntentDescriptor {
|
|
6
6
|
readonly kind: 'zeroshot.run-intent/v2';
|
|
@@ -14,12 +14,13 @@ export interface RunIntentDescriptor {
|
|
|
14
14
|
|
|
15
15
|
export function parseRunIntent(value: unknown, origin: string): RunIntentDescriptor | null {
|
|
16
16
|
if (value === undefined || value === null) return null;
|
|
17
|
+
const advertised = record(value, 'extensions.run_intent');
|
|
18
|
+
if (advertised.kind !== 'zeroshot.run-intent/v2') return null;
|
|
17
19
|
const extension = closedRecord(value, 'extensions.run_intent', [
|
|
18
20
|
'kind',
|
|
19
21
|
'base_url',
|
|
20
22
|
'route_templates',
|
|
21
23
|
]);
|
|
22
|
-
exact(extension.kind, 'zeroshot.run-intent/v2', 'extensions.run_intent.kind');
|
|
23
24
|
const routes = closedRecord(extension.route_templates, 'extensions.run_intent.route_templates', [
|
|
24
25
|
'submit',
|
|
25
26
|
'status',
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.installRuntime = installRuntime;
|
|
4
|
-
const bounded_response_js_1 = require("../target/bounded-response.cjs");
|
|
5
|
-
const bounds_js_1 = require("./bounds.cjs");
|
|
6
|
-
const errors_js_1 = require("./errors.cjs");
|
|
7
|
-
const response_status_js_1 = require("./response-status.cjs");
|
|
8
|
-
const retry_executor_js_1 = require("./retry-executor.cjs");
|
|
9
|
-
function validOpaque(value, field) {
|
|
10
|
-
if (value.length === 0 || value.length > 1024) {
|
|
11
|
-
throw new errors_js_1.TargetProtocolError(`${field} is invalid`);
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
function runtimeBody(runtime, maximum) {
|
|
15
|
-
let body;
|
|
16
|
-
try {
|
|
17
|
-
body = JSON.stringify(runtime);
|
|
18
|
-
}
|
|
19
|
-
catch {
|
|
20
|
-
throw new errors_js_1.TargetProtocolError('Runtime bundle is not serializable');
|
|
21
|
-
}
|
|
22
|
-
if (body === undefined || Buffer.byteLength(body) > maximum) {
|
|
23
|
-
throw new errors_js_1.TargetProtocolError('Runtime bundle exceeds the advertised size bound');
|
|
24
|
-
}
|
|
25
|
-
return body;
|
|
26
|
-
}
|
|
27
|
-
async function installRuntime(options) {
|
|
28
|
-
validOpaque(options.capsuleId, 'capsule id');
|
|
29
|
-
validOpaque(options.accessToken, 'capsule access token');
|
|
30
|
-
let path;
|
|
31
|
-
try {
|
|
32
|
-
path = options.descriptor.install.routeTemplate.expand({
|
|
33
|
-
capsule_id: options.capsuleId,
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
throw new errors_js_1.TargetProtocolError('Runtime install route expansion is unsafe');
|
|
38
|
-
}
|
|
39
|
-
const body = runtimeBody(options.runtime, options.descriptor.maxBodyBytes);
|
|
40
|
-
await (0, retry_executor_js_1.withTargetRetry)('installRuntime', async () => {
|
|
41
|
-
const response = await options.request(options.descriptor.install.method, path, options.signal, body, options.accessToken);
|
|
42
|
-
await (0, response_status_js_1.assertCapsuleResponseStatus)(response, 204, (errorResponse) => (0, bounded_response_js_1.readBoundedResponseJson)(errorResponse, bounds_js_1.MAX_RESPONSE_BYTES, () => new errors_js_1.TargetProtocolError('Capsule error response is malformed')), options.clock);
|
|
43
|
-
await response.body?.cancel().catch(() => undefined);
|
|
44
|
-
}, options.signal, { clock: options.clock, policy: options.retryPolicy });
|
|
45
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { CredentialInstallDescriptor } from '../target/discovery.js';
|
|
2
|
-
import type { Clock, RetryPolicy } from './types.js';
|
|
3
|
-
type RuntimeInstallOptions = {
|
|
4
|
-
readonly capsuleId: string;
|
|
5
|
-
readonly runtime: unknown;
|
|
6
|
-
readonly accessToken: string;
|
|
7
|
-
readonly descriptor: CredentialInstallDescriptor;
|
|
8
|
-
readonly signal?: AbortSignal;
|
|
9
|
-
readonly clock: Clock;
|
|
10
|
-
readonly retryPolicy: RetryPolicy;
|
|
11
|
-
readonly request: (method: string, path: string, signal: AbortSignal | undefined, body: string, accessToken: string) => Promise<Response>;
|
|
12
|
-
};
|
|
13
|
-
export declare function installRuntime(options: RuntimeInstallOptions): Promise<void>;
|
|
14
|
-
export {};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { CredentialInstallDescriptor } from '../target/discovery.js';
|
|
2
|
-
import type { Clock, RetryPolicy } from './types.js';
|
|
3
|
-
type RuntimeInstallOptions = {
|
|
4
|
-
readonly capsuleId: string;
|
|
5
|
-
readonly runtime: unknown;
|
|
6
|
-
readonly accessToken: string;
|
|
7
|
-
readonly descriptor: CredentialInstallDescriptor;
|
|
8
|
-
readonly signal?: AbortSignal;
|
|
9
|
-
readonly clock: Clock;
|
|
10
|
-
readonly retryPolicy: RetryPolicy;
|
|
11
|
-
readonly request: (method: string, path: string, signal: AbortSignal | undefined, body: string, accessToken: string) => Promise<Response>;
|
|
12
|
-
};
|
|
13
|
-
export declare function installRuntime(options: RuntimeInstallOptions): Promise<void>;
|
|
14
|
-
export {};
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import type { CredentialInstallDescriptor } from '../target/discovery.js';
|
|
2
|
-
import type { Clock, RetryPolicy } from './types.js';
|
|
3
|
-
type RuntimeInstallOptions = {
|
|
4
|
-
readonly capsuleId: string;
|
|
5
|
-
readonly runtime: unknown;
|
|
6
|
-
readonly accessToken: string;
|
|
7
|
-
readonly descriptor: CredentialInstallDescriptor;
|
|
8
|
-
readonly signal?: AbortSignal;
|
|
9
|
-
readonly clock: Clock;
|
|
10
|
-
readonly retryPolicy: RetryPolicy;
|
|
11
|
-
readonly request: (method: string, path: string, signal: AbortSignal | undefined, body: string, accessToken: string) => Promise<Response>;
|
|
12
|
-
};
|
|
13
|
-
export declare function installRuntime(options: RuntimeInstallOptions): Promise<void>;
|
|
14
|
-
export {};
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { readBoundedResponseJson } from '../target/bounded-response.mjs';
|
|
2
|
-
import { MAX_RESPONSE_BYTES } from './bounds.mjs';
|
|
3
|
-
import { TargetProtocolError } from './errors.mjs';
|
|
4
|
-
import { assertCapsuleResponseStatus } from './response-status.mjs';
|
|
5
|
-
import { withTargetRetry } from './retry-executor.mjs';
|
|
6
|
-
function validOpaque(value, field) {
|
|
7
|
-
if (value.length === 0 || value.length > 1024) {
|
|
8
|
-
throw new TargetProtocolError(`${field} is invalid`);
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
function runtimeBody(runtime, maximum) {
|
|
12
|
-
let body;
|
|
13
|
-
try {
|
|
14
|
-
body = JSON.stringify(runtime);
|
|
15
|
-
}
|
|
16
|
-
catch {
|
|
17
|
-
throw new TargetProtocolError('Runtime bundle is not serializable');
|
|
18
|
-
}
|
|
19
|
-
if (body === undefined || Buffer.byteLength(body) > maximum) {
|
|
20
|
-
throw new TargetProtocolError('Runtime bundle exceeds the advertised size bound');
|
|
21
|
-
}
|
|
22
|
-
return body;
|
|
23
|
-
}
|
|
24
|
-
export async function installRuntime(options) {
|
|
25
|
-
validOpaque(options.capsuleId, 'capsule id');
|
|
26
|
-
validOpaque(options.accessToken, 'capsule access token');
|
|
27
|
-
let path;
|
|
28
|
-
try {
|
|
29
|
-
path = options.descriptor.install.routeTemplate.expand({
|
|
30
|
-
capsule_id: options.capsuleId,
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
throw new TargetProtocolError('Runtime install route expansion is unsafe');
|
|
35
|
-
}
|
|
36
|
-
const body = runtimeBody(options.runtime, options.descriptor.maxBodyBytes);
|
|
37
|
-
await withTargetRetry('installRuntime', async () => {
|
|
38
|
-
const response = await options.request(options.descriptor.install.method, path, options.signal, body, options.accessToken);
|
|
39
|
-
await assertCapsuleResponseStatus(response, 204, (errorResponse) => readBoundedResponseJson(errorResponse, MAX_RESPONSE_BYTES, () => new TargetProtocolError('Capsule error response is malformed')), options.clock);
|
|
40
|
-
await response.body?.cancel().catch(() => undefined);
|
|
41
|
-
}, options.signal, { clock: options.clock, policy: options.retryPolicy });
|
|
42
|
-
}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import type { CredentialInstallDescriptor } from '../target/discovery.js';
|
|
2
|
-
import { readBoundedResponseJson } from '../target/bounded-response.js';
|
|
3
|
-
import { MAX_RESPONSE_BYTES } from './bounds.js';
|
|
4
|
-
import { TargetProtocolError } from './errors.js';
|
|
5
|
-
import { assertCapsuleResponseStatus } from './response-status.js';
|
|
6
|
-
import { withTargetRetry } from './retry-executor.js';
|
|
7
|
-
import type { Clock, RetryPolicy } from './types.js';
|
|
8
|
-
|
|
9
|
-
type RuntimeInstallOptions = {
|
|
10
|
-
readonly capsuleId: string;
|
|
11
|
-
readonly runtime: unknown;
|
|
12
|
-
readonly accessToken: string;
|
|
13
|
-
readonly descriptor: CredentialInstallDescriptor;
|
|
14
|
-
readonly signal?: AbortSignal;
|
|
15
|
-
readonly clock: Clock;
|
|
16
|
-
readonly retryPolicy: RetryPolicy;
|
|
17
|
-
readonly request: (
|
|
18
|
-
method: string,
|
|
19
|
-
path: string,
|
|
20
|
-
signal: AbortSignal | undefined,
|
|
21
|
-
body: string,
|
|
22
|
-
accessToken: string
|
|
23
|
-
) => Promise<Response>;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
function validOpaque(value: string, field: string): void {
|
|
27
|
-
if (value.length === 0 || value.length > 1024) {
|
|
28
|
-
throw new TargetProtocolError(`${field} is invalid`);
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function runtimeBody(runtime: unknown, maximum: number): string {
|
|
33
|
-
let body: string | undefined;
|
|
34
|
-
try {
|
|
35
|
-
body = JSON.stringify(runtime);
|
|
36
|
-
} catch {
|
|
37
|
-
throw new TargetProtocolError('Runtime bundle is not serializable');
|
|
38
|
-
}
|
|
39
|
-
if (body === undefined || Buffer.byteLength(body) > maximum) {
|
|
40
|
-
throw new TargetProtocolError('Runtime bundle exceeds the advertised size bound');
|
|
41
|
-
}
|
|
42
|
-
return body;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
export async function installRuntime(options: RuntimeInstallOptions): Promise<void> {
|
|
46
|
-
validOpaque(options.capsuleId, 'capsule id');
|
|
47
|
-
validOpaque(options.accessToken, 'capsule access token');
|
|
48
|
-
let path: string;
|
|
49
|
-
try {
|
|
50
|
-
path = options.descriptor.install.routeTemplate.expand({
|
|
51
|
-
capsule_id: options.capsuleId,
|
|
52
|
-
});
|
|
53
|
-
} catch {
|
|
54
|
-
throw new TargetProtocolError('Runtime install route expansion is unsafe');
|
|
55
|
-
}
|
|
56
|
-
const body = runtimeBody(options.runtime, options.descriptor.maxBodyBytes);
|
|
57
|
-
await withTargetRetry(
|
|
58
|
-
'installRuntime',
|
|
59
|
-
async () => {
|
|
60
|
-
const response = await options.request(
|
|
61
|
-
options.descriptor.install.method,
|
|
62
|
-
path,
|
|
63
|
-
options.signal,
|
|
64
|
-
body,
|
|
65
|
-
options.accessToken
|
|
66
|
-
);
|
|
67
|
-
await assertCapsuleResponseStatus(
|
|
68
|
-
response,
|
|
69
|
-
204,
|
|
70
|
-
(errorResponse) =>
|
|
71
|
-
readBoundedResponseJson(
|
|
72
|
-
errorResponse,
|
|
73
|
-
MAX_RESPONSE_BYTES,
|
|
74
|
-
() => new TargetProtocolError('Capsule error response is malformed')
|
|
75
|
-
),
|
|
76
|
-
options.clock
|
|
77
|
-
);
|
|
78
|
-
await response.body?.cancel().catch(() => undefined);
|
|
79
|
-
},
|
|
80
|
-
options.signal,
|
|
81
|
-
{ clock: options.clock, policy: options.retryPolicy }
|
|
82
|
-
);
|
|
83
|
-
}
|