@robota-sdk/agent-transport 3.0.0-beta.100
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/CHANGELOG.md +1137 -0
- package/LICENSE +661 -0
- package/README.md +121 -0
- package/dist/browser/client.d.ts +2 -0
- package/dist/browser/client.js +1 -0
- package/dist/browser/index.d.ts +624 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +2 -0
- package/dist/browser/index.js.map +1 -0
- package/dist/browser/observer-messages-4fRQZU2D.d.ts +499 -0
- package/dist/browser/observer-messages-4fRQZU2D.d.ts.map +1 -0
- package/dist/browser/observer-messages-O8jTPEQO.js +2 -0
- package/dist/browser/observer-messages-O8jTPEQO.js.map +1 -0
- package/dist/node/index.cjs +1 -0
- package/dist/node/index.d.cts +1115 -0
- package/dist/node/index.d.cts.map +1 -0
- package/dist/node/index.d.ts +1115 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +2 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/node.cjs +1 -0
- package/dist/node/node.d.cts +317 -0
- package/dist/node/node.d.cts.map +1 -0
- package/dist/node/node.d.ts +317 -0
- package/dist/node/node.d.ts.map +1 -0
- package/dist/node/node.js +2 -0
- package/dist/node/node.js.map +1 -0
- package/package.json +122 -0
- package/src/__tests__/__fixtures__/korean-text.json +6 -0
- package/src/__tests__/access-token-verifier.test.ts +672 -0
- package/src/__tests__/admission.test.ts +130 -0
- package/src/__tests__/app-tool-messages.test.ts +463 -0
- package/src/__tests__/bearer-resource-gate.test.ts +120 -0
- package/src/__tests__/channel-frames.test.ts +215 -0
- package/src/__tests__/driver-attribution-capability.test.ts +83 -0
- package/src/__tests__/file-transfer.test.ts +281 -0
- package/src/__tests__/handoff-chunking.test.ts +261 -0
- package/src/__tests__/handoff-manifest.test.ts +52 -0
- package/src/__tests__/history-attached-budget.test.ts +216 -0
- package/src/__tests__/message-decoders.test.ts +870 -0
- package/src/__tests__/outbound-delivery.test.ts +558 -0
- package/src/__tests__/peer-message-ledger.test.ts +200 -0
- package/src/__tests__/permission-summary.test.ts +18 -0
- package/src/__tests__/personal-usage-report.test.ts +193 -0
- package/src/__tests__/project-read-messages.test.ts +118 -0
- package/src/__tests__/resume-buffer.test.ts +93 -0
- package/src/__tests__/session-detail-loop-status.test.ts +169 -0
- package/src/__tests__/session-directory-messages.test.ts +322 -0
- package/src/__tests__/session-error-classification.test.ts +113 -0
- package/src/__tests__/session-event-delivery.test.ts +101 -0
- package/src/__tests__/session-history-wire.test.ts +364 -0
- package/src/__tests__/session-message-handler-e5.test.ts +99 -0
- package/src/__tests__/session-message-handler-observe.test.ts +223 -0
- package/src/__tests__/session-message-handler.test.ts +726 -0
- package/src/__tests__/session-resume-bridge.test.ts +472 -0
- package/src/__tests__/settings-messages.test.ts +193 -0
- package/src/__tests__/stdio-carrier.test.ts +85 -0
- package/src/__tests__/submit-prompt-shape.test.ts +78 -0
- package/src/__tests__/usage-report-carrier.test.ts +98 -0
- package/src/__tests__/ws-broadcast-events.test.ts +94 -0
- package/src/app-tool-messages.ts +140 -0
- package/src/background-messages.ts +169 -0
- package/src/channel-frames.ts +182 -0
- package/src/client.ts +17 -0
- package/src/handoff-chunking.ts +275 -0
- package/src/index.ts +135 -0
- package/src/loop-control-messages.ts +34 -0
- package/src/message-decoders.ts +485 -0
- package/src/message-parser.ts +33 -0
- package/src/node/access-token-verifier.ts +363 -0
- package/src/node/admission.ts +153 -0
- package/src/node/bearer-resource-gate.ts +331 -0
- package/src/node/file-transfer.ts +530 -0
- package/src/node/handoff-manifest.ts +77 -0
- package/src/node/index.ts +45 -0
- package/src/node/stdio-carrier.ts +59 -0
- package/src/observer-messages.ts +47 -0
- package/src/open-prompts.ts +76 -0
- package/src/outbound-delivery.ts +240 -0
- package/src/peer-message-ledger.ts +193 -0
- package/src/permission-summary.ts +18 -0
- package/src/project-read-messages.ts +74 -0
- package/src/protocol-session.ts +56 -0
- package/src/resume-buffer.ts +123 -0
- package/src/session-directory-messages.ts +162 -0
- package/src/session-error-classification.ts +91 -0
- package/src/session-events.ts +290 -0
- package/src/session-message-handler.ts +413 -0
- package/src/session-query-messages.ts +160 -0
- package/src/session-resume-bridge.ts +275 -0
- package/src/settings-messages.ts +103 -0
- package/src/usage-messages.ts +195 -0
- package/src/wire-messages.ts +436 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { IInteractiveSessionRecord } from "@robota-sdk/agent-interface-session";
|
|
2
|
+
import { IAccessTokenVerifier, IAccessTokenVerifierConfig, ITransportAdmission, ITransportAdmissionConfig, TAccessTokenRefusal } from "@robota-sdk/agent-interface-transport";
|
|
3
|
+
import { IFileFrameChannel, IFileOffer, IHandoffIntegrity } from "@robota-sdk/agent-interface-session-mobility";
|
|
4
|
+
import { IEgressDeps } from "@robota-sdk/agent-core/node";
|
|
5
|
+
import { IncomingMessage, ServerResponse } from "node:http";
|
|
6
|
+
import { PassThrough, Readable, Writable } from "node:stream";
|
|
7
|
+
//#region src/node/admission.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* Mint a per-launch credential.
|
|
10
|
+
*
|
|
11
|
+
* Throws rather than returning a short or empty string if entropy is unavailable, so a transport
|
|
12
|
+
* built on it fails to construct instead of binding with a guessable credential.
|
|
13
|
+
*/
|
|
14
|
+
declare function mintTransportToken(): string;
|
|
15
|
+
/**
|
|
16
|
+
* Resolve the admission decision for one transport.
|
|
17
|
+
*
|
|
18
|
+
* IDEMPOTENT: it accepts an already-resolved `ITransportAdmission` and returns it unchanged, so
|
|
19
|
+
* `resolve(resolve(x))` is `resolve(x)`. That is not a convenience — it is what lets a caller
|
|
20
|
+
* holding either shape just call this, instead of deciding which shape it holds first.
|
|
21
|
+
*
|
|
22
|
+
* The HTTP route used to make that decision with `'token' in options.admission`, and review showed
|
|
23
|
+
* it cannot work: BOTH interfaces declare a `token`, so a config is only distinguishable from a
|
|
24
|
+
* resolution by the VALUE. `{ token: '' }` — documented as "mint a fresh one" — was read as
|
|
25
|
+
* pre-resolved and installed the empty string as the required credential, which a peer sending an
|
|
26
|
+
* empty bearer would then match. Making this function idempotent deletes that discriminator
|
|
27
|
+
* instead of repairing it.
|
|
28
|
+
*
|
|
29
|
+
* @throws if `open` is set without a reason — an unexplained open transport is the state this
|
|
30
|
+
* module exists to remove, so it is refused at construction rather than recorded and shipped.
|
|
31
|
+
* @throws if a token and `open: true` are asked for together. The two are contradictory, and the
|
|
32
|
+
* silent precedence this used to give (token wins) is the safe DIRECTION but the wrong ANSWER: a
|
|
33
|
+
* caller that wrote both does not know what it is asking for, and one of the two things it believes
|
|
34
|
+
* is false. `WebRtcTransport` already refused the analogous `secret` + `open` pair in its own
|
|
35
|
+
* constructor; a seam whose whole claim is "one place to read and one place to change" cannot leave
|
|
36
|
+
* that protection to whichever transport happens to have implemented it.
|
|
37
|
+
*/
|
|
38
|
+
declare function resolveAdmission(config?: ITransportAdmissionConfig | ITransportAdmission): ITransportAdmission;
|
|
39
|
+
/**
|
|
40
|
+
* Whether a presented credential matches the required one.
|
|
41
|
+
*
|
|
42
|
+
* Compared with `timingSafeEqual` over the raw bytes, so the answer takes the same time whatever the
|
|
43
|
+
* first differing character is. The length check before it is unavoidable — `timingSafeEqual` throws
|
|
44
|
+
* on unequal lengths — and it does leak one bit: whether the presented credential is the right
|
|
45
|
+
* LENGTH.
|
|
46
|
+
*
|
|
47
|
+
* For a minted token that costs nothing: the length is fixed at 64 hex characters and published in
|
|
48
|
+
* this file. A host that supplies its own token of some other length leaks that length instead, and
|
|
49
|
+
* review was right to point out that the earlier wording ("fixed and public") quietly assumed the
|
|
50
|
+
* minted case for every case. Length is not the secret in either — but the claim now says which
|
|
51
|
+
* situation it is describing.
|
|
52
|
+
*
|
|
53
|
+
* A missing credential is a mismatch, not a special case: a peer that sends nothing and a peer that
|
|
54
|
+
* sends the wrong thing are equally unadmitted, and giving them different answers would tell an
|
|
55
|
+
* attacker which half they got right.
|
|
56
|
+
*/
|
|
57
|
+
declare function credentialMatches(expected: string, presented: string | undefined): boolean;
|
|
58
|
+
declare function bearerCredential(header: string | undefined | null): string | undefined;
|
|
59
|
+
//#endregion
|
|
60
|
+
//#region src/node/handoff-manifest.d.ts
|
|
61
|
+
/**
|
|
62
|
+
* Serialize a record and describe what was serialized.
|
|
63
|
+
*
|
|
64
|
+
* The string is returned alongside the integrity data because the digest is only meaningful for
|
|
65
|
+
* these exact bytes. A caller that re-serialized the record before sending could produce a
|
|
66
|
+
* different key order and a digest mismatch on a payload that is perfectly correct — a corruption
|
|
67
|
+
* report caused by the corruption check.
|
|
68
|
+
*/
|
|
69
|
+
declare function sealHandoffRecord(record: IInteractiveSessionRecord): {
|
|
70
|
+
serialized: string;
|
|
71
|
+
integrity: IHandoffIntegrity;
|
|
72
|
+
};
|
|
73
|
+
/** Why a received payload was not accepted. */
|
|
74
|
+
type TIntegrityFailure = 'truncated' | 'digest-mismatch';
|
|
75
|
+
interface IIntegrityVerdict {
|
|
76
|
+
readonly intact: boolean;
|
|
77
|
+
readonly failure?: TIntegrityFailure;
|
|
78
|
+
/** What the manifest promised and what arrived, so a report can say more than "it failed". */
|
|
79
|
+
readonly expectedBytes?: number;
|
|
80
|
+
readonly actualBytes?: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check a received payload against the manifest's integrity metadata.
|
|
84
|
+
*
|
|
85
|
+
* Nothing is parsed here. Parsing a payload to check it is well-formed would mean building the
|
|
86
|
+
* object graph before knowing whether the bytes are the ones that were sent, and a destination that
|
|
87
|
+
* has already parsed a corrupt payload has already spent the effort this check exists to avoid.
|
|
88
|
+
*/
|
|
89
|
+
declare function verifyHandoffPayload(serialized: string, integrity: IHandoffIntegrity): IIntegrityVerdict;
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/node/access-token-verifier.d.ts
|
|
92
|
+
/** What the verifier reaches outward through. Injected in tests; defaults to the real network. */
|
|
93
|
+
interface IAccessTokenVerifierDeps {
|
|
94
|
+
readonly fetch?: IEgressDeps['fetch'];
|
|
95
|
+
readonly lookup?: IEgressDeps['lookup'];
|
|
96
|
+
/** Wall-clock milliseconds since the epoch, for `exp`/`nbf`. Defaults to `Date.now`. */
|
|
97
|
+
readonly now?: () => number;
|
|
98
|
+
/** Monotonic milliseconds, for cache age and refetch intervals. Defaults to `performance.now`. */
|
|
99
|
+
readonly monotonicNow?: () => number;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Build a verifier for one issuer and one resource.
|
|
103
|
+
*
|
|
104
|
+
* @throws when the configuration cannot describe a safe verifier: a non-`https` issuer, no
|
|
105
|
+
* algorithm or an unsupported one, no required scope, or no allowlisted subject or client. The
|
|
106
|
+
* last is the single-tenant rule: without it any holder of a token from the issuer is admitted.
|
|
107
|
+
*/
|
|
108
|
+
declare function createAccessTokenVerifier(config: IAccessTokenVerifierConfig, deps?: IAccessTokenVerifierDeps): IAccessTokenVerifier;
|
|
109
|
+
//#endregion
|
|
110
|
+
//#region src/node/bearer-resource-gate.d.ts
|
|
111
|
+
/** A coarse class of a peer's address; the address itself is never reported. */
|
|
112
|
+
type TRemoteAddressClass = 'loopback' | 'private' | 'public' | 'unknown';
|
|
113
|
+
/** Where a refused request came from, and whether its address is over the failure budget. */
|
|
114
|
+
interface IBearerFailure {
|
|
115
|
+
readonly remote: TRemoteAddressClass;
|
|
116
|
+
readonly throttled: boolean;
|
|
117
|
+
readonly retryAfterSeconds: number;
|
|
118
|
+
}
|
|
119
|
+
/** The public-facing identity of one resource server and its per-address failure budget. */
|
|
120
|
+
interface IBearerResourceServer {
|
|
121
|
+
/** The parsed public URL; its path is the resource's base path. */
|
|
122
|
+
readonly url: URL;
|
|
123
|
+
/**
|
|
124
|
+
* Host and Origin must name the public URL. On a mismatch the 403 is written and false returned;
|
|
125
|
+
* nothing is counted, since no credential was judged.
|
|
126
|
+
*/
|
|
127
|
+
checkNames(req: IncomingMessage, res: ServerResponse): boolean;
|
|
128
|
+
/** Count one failure against the request's peer. */
|
|
129
|
+
fail(req: IncomingMessage): IBearerFailure;
|
|
130
|
+
/** The request's peer class without counting anything. */
|
|
131
|
+
remote(req: IncomingMessage): TRemoteAddressClass;
|
|
132
|
+
}
|
|
133
|
+
/** The challenges and metadata of one protected resource (one audience). */
|
|
134
|
+
interface IProtectedResource {
|
|
135
|
+
/** The RFC 9728 path clients fetch the metadata from. */
|
|
136
|
+
readonly wellKnownPath: string;
|
|
137
|
+
readonly metadata: string;
|
|
138
|
+
readonly challenges: {
|
|
139
|
+
readonly missing: string;
|
|
140
|
+
readonly invalid: string;
|
|
141
|
+
readonly scope: string;
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/** Parse a resource server's public URL: plain `https`, no credentials, query or fragment. */
|
|
145
|
+
declare function parsePublicHttpsUrl(publicUrl: string, label: string): URL;
|
|
146
|
+
/**
|
|
147
|
+
* Build the public identity of a resource server. Throws when the public URL is not plain `https`,
|
|
148
|
+
* or a trusted proxy is not a literal address. `label` names the carrier in those messages.
|
|
149
|
+
*/
|
|
150
|
+
declare function createBearerResourceServer(options: {
|
|
151
|
+
readonly publicUrl: string;
|
|
152
|
+
/** Proxy addresses whose `X-Forwarded-For` is believed. Absent: the header is ignored. */
|
|
153
|
+
readonly trustedProxies?: readonly string[];
|
|
154
|
+
readonly label: string;
|
|
155
|
+
readonly now?: () => number;
|
|
156
|
+
}): IBearerResourceServer;
|
|
157
|
+
/**
|
|
158
|
+
* Describe one protected resource: where its RFC 9728 metadata lives and the challenges that point
|
|
159
|
+
* there. Throws for a non-`https` issuer, or no scope or a scope a challenge cannot quote.
|
|
160
|
+
*/
|
|
161
|
+
declare function describeProtectedResource(options: {
|
|
162
|
+
/** The resource identifier (`aud`), an `https` URL. */
|
|
163
|
+
readonly resource: string;
|
|
164
|
+
readonly issuer: string;
|
|
165
|
+
readonly scopes: readonly string[];
|
|
166
|
+
readonly label: string;
|
|
167
|
+
}): IProtectedResource;
|
|
168
|
+
/** Answer a metadata request: GET only, the JSON document, nothing else. */
|
|
169
|
+
declare function serveProtectedResourceMetadata(req: IncomingMessage, res: ServerResponse, resource: IProtectedResource): void;
|
|
170
|
+
/**
|
|
171
|
+
* Answer a refused bearer token after its failure was counted: 429 once the peer is over budget,
|
|
172
|
+
* 403 `insufficient_scope` for a missing scope, otherwise 401 with the missing-token or
|
|
173
|
+
* `invalid_token` challenge. Always an empty body.
|
|
174
|
+
*/
|
|
175
|
+
declare function refuseBearerToken(res: ServerResponse, resource: IProtectedResource, refusal: 'missing-token' | 'missing-scope' | 'invalid-token', failure: IBearerFailure): void;
|
|
176
|
+
/** Why a request was refused: a verifier refusal, or no bearer token at all. */
|
|
177
|
+
type TBearerRefusal = TAccessTokenRefusal | 'missing-token';
|
|
178
|
+
/** One refused request as an audit sink sees it. Carries no token text and no claim value. */
|
|
179
|
+
interface IBearerRefusalRecord {
|
|
180
|
+
readonly refusal: TBearerRefusal;
|
|
181
|
+
readonly remote: TRemoteAddressClass;
|
|
182
|
+
/** True when the refusal was answered 429 because the address exceeded its failure budget. */
|
|
183
|
+
readonly throttled: boolean;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Answer one refused request and report it. When the issuer's keys are unavailable the issuer, not
|
|
187
|
+
* the peer, failed: 503, not counted against the peer, and not a token verdict. Every other refusal
|
|
188
|
+
* is counted and answered with the matching challenge.
|
|
189
|
+
*/
|
|
190
|
+
declare function refuseAccessToken(req: IncomingMessage, res: ServerResponse, server: IBearerResourceServer, resource: IProtectedResource, refusal: TBearerRefusal, audit?: (record: IBearerRefusalRecord) => void): void;
|
|
191
|
+
//#endregion
|
|
192
|
+
//#region src/node/file-transfer.d.ts
|
|
193
|
+
/** Why a transfer did not end with the file kept. */
|
|
194
|
+
type TFileTransferRefusal =
|
|
195
|
+
/** The receiving operator said no, could not be asked, or the connection does not carry files. */
|
|
196
|
+
'declined' |
|
|
197
|
+
/** The file is over the size limit. */
|
|
198
|
+
'too-large' |
|
|
199
|
+
/** The name is empty, or would leave the receiving directory. */
|
|
200
|
+
'bad-name' |
|
|
201
|
+
/** A file of that name was already received; nothing is overwritten. */
|
|
202
|
+
'exists' |
|
|
203
|
+
/** The receiving directory is not one this user owns outright, e.g. a symbolic link. */
|
|
204
|
+
'unsafe-path' |
|
|
205
|
+
/** The content did not match the offered size or hash. The partial file is discarded. */
|
|
206
|
+
'integrity' |
|
|
207
|
+
/** A frame broke the protocol. */
|
|
208
|
+
'protocol' |
|
|
209
|
+
/** The channel ended first. */
|
|
210
|
+
'closed' |
|
|
211
|
+
/** The other side went quiet. */
|
|
212
|
+
'timeout' |
|
|
213
|
+
/** The receiver could not store the file. */
|
|
214
|
+
'unavailable';
|
|
215
|
+
/**
|
|
216
|
+
* The default size limit. A transfer does not resume, so a dropped connection costs the whole file
|
|
217
|
+
* again; a received file also sits on the receiver's disk until its operator removes it. The limit
|
|
218
|
+
* keeps both costs small while covering what is sent between one's own sessions: sources, logs,
|
|
219
|
+
* screenshots, a small archive.
|
|
220
|
+
*/
|
|
221
|
+
declare const DEFAULT_MAX_FILE_BYTES: number;
|
|
222
|
+
/**
|
|
223
|
+
* Bytes of content per chunk. Small enough that a frame, base64 and envelope included, stays well
|
|
224
|
+
* inside the message size every channel it runs on carries.
|
|
225
|
+
*/
|
|
226
|
+
declare const FILE_CHUNK_BYTES: number;
|
|
227
|
+
/** Chunks the sender may have unconfirmed. */
|
|
228
|
+
declare const FILE_CREDIT_WINDOW = 16;
|
|
229
|
+
/** Where content comes from. `read` yields the content once, in order, in pieces of any size. */
|
|
230
|
+
interface IFileSource {
|
|
231
|
+
readonly size: number;
|
|
232
|
+
read(): AsyncIterable<Uint8Array>;
|
|
233
|
+
}
|
|
234
|
+
/** Where received content goes until it is verified; nothing is kept unless `commit` runs. */
|
|
235
|
+
interface IFileSink {
|
|
236
|
+
write(chunk: Uint8Array): Promise<void>;
|
|
237
|
+
/** Keep the verified file. Resolves with where it is. */
|
|
238
|
+
commit(): Promise<string>;
|
|
239
|
+
/** Drop what was written. */
|
|
240
|
+
discard(): Promise<void>;
|
|
241
|
+
}
|
|
242
|
+
/** The receiver's decision on an offer: somewhere to put it, or why not. */
|
|
243
|
+
type TFileAdmission = {
|
|
244
|
+
readonly sink: IFileSink;
|
|
245
|
+
} | {
|
|
246
|
+
readonly refused: TFileTransferRefusal;
|
|
247
|
+
readonly detail?: string;
|
|
248
|
+
};
|
|
249
|
+
type TFileSendOutcome = {
|
|
250
|
+
readonly ok: true;
|
|
251
|
+
} | {
|
|
252
|
+
readonly ok: false;
|
|
253
|
+
readonly reason: TFileTransferRefusal;
|
|
254
|
+
readonly detail?: string;
|
|
255
|
+
};
|
|
256
|
+
type TFileReceiveOutcome = {
|
|
257
|
+
readonly ok: true;
|
|
258
|
+
readonly offer: IFileOffer;
|
|
259
|
+
readonly location: string;
|
|
260
|
+
} | {
|
|
261
|
+
readonly ok: false;
|
|
262
|
+
readonly reason: TFileTransferRefusal;
|
|
263
|
+
readonly detail?: string;
|
|
264
|
+
readonly offer?: IFileOffer;
|
|
265
|
+
};
|
|
266
|
+
interface ISendFileOptions {
|
|
267
|
+
readonly channel: IFileFrameChannel;
|
|
268
|
+
readonly offer: IFileOffer;
|
|
269
|
+
readonly source: IFileSource;
|
|
270
|
+
readonly chunkBytes?: number;
|
|
271
|
+
readonly window?: number;
|
|
272
|
+
readonly idleMs?: number;
|
|
273
|
+
/** How long the receiving operator may take to answer. */
|
|
274
|
+
readonly decisionMs?: number;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Offer a file and, once accepted, send it. Resolves when the receiver confirms it kept the file, or
|
|
278
|
+
* with why it did not. Closes the channel either way.
|
|
279
|
+
*/
|
|
280
|
+
declare function sendFileOverChannel(options: ISendFileOptions): Promise<TFileSendOutcome>;
|
|
281
|
+
interface IReceiveFileOptions {
|
|
282
|
+
readonly channel: IFileFrameChannel;
|
|
283
|
+
/** Offers above this are refused without asking anyone. */
|
|
284
|
+
readonly maxBytes?: number;
|
|
285
|
+
/**
|
|
286
|
+
* Decide on a well-formed offer within the size limit: ask whoever must approve it, and open
|
|
287
|
+
* somewhere to put it. `signal` aborts when the sender goes away.
|
|
288
|
+
*/
|
|
289
|
+
readonly admit: (offer: IFileOffer, signal: AbortSignal) => Promise<TFileAdmission>;
|
|
290
|
+
readonly window?: number;
|
|
291
|
+
readonly idleMs?: number;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Receive one file: the channel's first frame is the offer. Resolves with where the verified file was
|
|
295
|
+
* kept, or with why nothing was. Closes the channel either way.
|
|
296
|
+
*/
|
|
297
|
+
declare function receiveFileOverChannel(options: IReceiveFileOptions): Promise<TFileReceiveOutcome>;
|
|
298
|
+
//#endregion
|
|
299
|
+
//#region src/node/stdio-carrier.d.ts
|
|
300
|
+
interface IStdioCarrierHandlers {
|
|
301
|
+
/** The peer ended its side: `end` or `close` on the input. May fire more than once. */
|
|
302
|
+
readonly onInputClose: () => void;
|
|
303
|
+
/** The output closed. */
|
|
304
|
+
readonly onOutputClose: () => void;
|
|
305
|
+
/** The output failed to accept a write. */
|
|
306
|
+
readonly onOutputError: (error: Error) => void;
|
|
307
|
+
}
|
|
308
|
+
interface IStdioCarrier {
|
|
309
|
+
/** Hand this to the protocol SDK in place of the input; destroying it never destroys the input. */
|
|
310
|
+
readonly bridge: PassThrough;
|
|
311
|
+
/** Remove the listeners, unpipe and destroy the bridge. Safe to call again; the streams stay open. */
|
|
312
|
+
release(): void;
|
|
313
|
+
}
|
|
314
|
+
declare function attachStdioCarrier(input: Readable, output: Writable, handlers: IStdioCarrierHandlers): IStdioCarrier;
|
|
315
|
+
//#endregion
|
|
316
|
+
export { DEFAULT_MAX_FILE_BYTES, FILE_CHUNK_BYTES, FILE_CREDIT_WINDOW, type IAccessTokenVerifierDeps, type IBearerFailure, type IBearerRefusalRecord, type IBearerResourceServer, type IFileSink, type IFileSource, type IIntegrityVerdict, type IProtectedResource, type IReceiveFileOptions, type ISendFileOptions, type IStdioCarrier, type IStdioCarrierHandlers, type TBearerRefusal, type TFileAdmission, type TFileReceiveOutcome, type TFileSendOutcome, type TFileTransferRefusal, type TIntegrityFailure, type TRemoteAddressClass, attachStdioCarrier, bearerCredential, createAccessTokenVerifier, createBearerResourceServer, credentialMatches, describeProtectedResource, mintTransportToken, parsePublicHttpsUrl, receiveFileOverChannel, refuseAccessToken, refuseBearerToken, resolveAdmission, sealHandoffRecord, sendFileOverChannel, serveProtectedResourceMetadata, verifyHandoffPayload };
|
|
317
|
+
//# sourceMappingURL=node.d.cts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.d.cts","names":[],"sources":["../../src/node/admission.ts","../../src/node/handoff-manifest.ts","../../src/node/access-token-verifier.ts","../../src/node/bearer-resource-gate.ts","../../src/node/file-transfer.ts","../../src/node/stdio-carrier.ts"],"mappings":";;;;;;;;;;;;;iBA4BgB;;;;;;;;;;;;;;;;;;;;;;;;iBA+BA,iBACd,SAAQ,4BAA4B,sBACnC;;;;;;;;;;;;;;;;;;;iBAoDa,kBAAkB,kBAAkB;iBA8BpC,iBAAiB;;;;;;;;;;;iBCpHjB,kBAAkB,QAAQ;EACxC;EACA,WAAW;;;KAcD;UAEK;WACN;WACA,UAAU;;WAEV;WACA;;;;;;;;;iBAUK,qBACd,oBACA,WAAW,oBACV;;;;UCCc;WACN,QAAQ;WACR,SAAS;;WAET;;WAEA;;;;;;;;;iBAwBK,0BACd,QAAQ,4BACR,OAAM,2BACL;;;;KC9DS;;UAGK;WACN,QAAQ;WACR;WACA;;;UAIM;;WAEN,KAAK;;;;;EAKd,WAAW,KAAK,iBAAiB,KAAK;;EAEtC,KAAK,KAAK,kBAAkB;;EAE5B,OAAO,KAAK,kBAAkB;;;UAIf;;WAEN;WACA;WACA;aACE;aACA;aACA;;;;iBAkCG,oBAAoB,mBAAmB,gBAAgB;;;;;iBAoDvD,2BAA2B;WAChC;;WAEA;WACA;WACA;IACP;;;;;iBA0DY,0BAA0B;;WAE/B;WACA;WACA;WACA;IACP;;iBAoCY,+BACd,KAAK,iBACL,KAAK,gBACL,UAAU;;;;;;iBAcI,kBACd,KAAK,gBACL,UAAU,oBACV,8DACA,SAAS;;KAcC,iBAAiB;;UAGZ;WACN,SAAS;WACT,QAAQ;;WAER;;;;;;;iBAQK,kBACd,KAAK,iBACL,KAAK,gBACL,QAAQ,uBACR,UAAU,oBACV,SAAS,gBACT,SAAS,QAAQ;;;;KC7RP;;;;;;;;;;;;;;;;;;;;;;;;;;;cA4BC;;;;;cAMA;;cAEA;;UAcI;WACN;EACT,QAAQ,cAAc;;;UAIP;EACf,MAAM,OAAO,aAAa;;EAE1B,UAAU;;EAEV,WAAW;;;KAID;WACG,MAAM;;WACN,SAAS;WAA+B;;KAE3C;WACG;;WACA;WAAoB,QAAQ;WAA+B;;KAE9D;WACG;WAAmB,OAAO;WAAqB;;WAE/C;WACA,QAAQ;WACR;WACA,QAAQ;;UA4NN;WACN,SAAS;WACT,OAAO;WACP,QAAQ;WACR;WACA;WACA;;WAEA;;;;;;iBAOW,oBAAoB,SAAS,mBAAmB,QAAQ;UA2D7D;WACN,SAAS;;WAET;;;;;WAKA,QAAQ,OAAO,YAAY,QAAQ,gBAAgB,QAAQ;WAC3D;WACA;;;;;;iBAOW,uBACpB,SAAS,sBACR,QAAQ;;;UCrZM;;WAEN;;WAEA;;WAEA,gBAAgB,OAAO;;UAGjB;;WAEN,QAAQ;;EAEjB;;iBAGc,mBACd,OAAO,UACP,QAAQ,UACR,UAAU,wBACT"}
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import { IInteractiveSessionRecord } from "@robota-sdk/agent-interface-session";
|
|
2
|
+
import { IEgressDeps } from "@robota-sdk/agent-core/node";
|
|
3
|
+
import { PassThrough, Readable, Writable } from "node:stream";
|
|
4
|
+
import { IAccessTokenVerifier, IAccessTokenVerifierConfig, ITransportAdmission, ITransportAdmissionConfig, TAccessTokenRefusal } from "@robota-sdk/agent-interface-transport";
|
|
5
|
+
import { IFileFrameChannel, IFileOffer, IHandoffIntegrity } from "@robota-sdk/agent-interface-session-mobility";
|
|
6
|
+
import { IncomingMessage, ServerResponse } from "node:http";
|
|
7
|
+
//#region src/node/admission.d.ts
|
|
8
|
+
/**
|
|
9
|
+
* Mint a per-launch credential.
|
|
10
|
+
*
|
|
11
|
+
* Throws rather than returning a short or empty string if entropy is unavailable, so a transport
|
|
12
|
+
* built on it fails to construct instead of binding with a guessable credential.
|
|
13
|
+
*/
|
|
14
|
+
declare function mintTransportToken(): string;
|
|
15
|
+
/**
|
|
16
|
+
* Resolve the admission decision for one transport.
|
|
17
|
+
*
|
|
18
|
+
* IDEMPOTENT: it accepts an already-resolved `ITransportAdmission` and returns it unchanged, so
|
|
19
|
+
* `resolve(resolve(x))` is `resolve(x)`. That is not a convenience — it is what lets a caller
|
|
20
|
+
* holding either shape just call this, instead of deciding which shape it holds first.
|
|
21
|
+
*
|
|
22
|
+
* The HTTP route used to make that decision with `'token' in options.admission`, and review showed
|
|
23
|
+
* it cannot work: BOTH interfaces declare a `token`, so a config is only distinguishable from a
|
|
24
|
+
* resolution by the VALUE. `{ token: '' }` — documented as "mint a fresh one" — was read as
|
|
25
|
+
* pre-resolved and installed the empty string as the required credential, which a peer sending an
|
|
26
|
+
* empty bearer would then match. Making this function idempotent deletes that discriminator
|
|
27
|
+
* instead of repairing it.
|
|
28
|
+
*
|
|
29
|
+
* @throws if `open` is set without a reason — an unexplained open transport is the state this
|
|
30
|
+
* module exists to remove, so it is refused at construction rather than recorded and shipped.
|
|
31
|
+
* @throws if a token and `open: true` are asked for together. The two are contradictory, and the
|
|
32
|
+
* silent precedence this used to give (token wins) is the safe DIRECTION but the wrong ANSWER: a
|
|
33
|
+
* caller that wrote both does not know what it is asking for, and one of the two things it believes
|
|
34
|
+
* is false. `WebRtcTransport` already refused the analogous `secret` + `open` pair in its own
|
|
35
|
+
* constructor; a seam whose whole claim is "one place to read and one place to change" cannot leave
|
|
36
|
+
* that protection to whichever transport happens to have implemented it.
|
|
37
|
+
*/
|
|
38
|
+
declare function resolveAdmission(config?: ITransportAdmissionConfig | ITransportAdmission): ITransportAdmission;
|
|
39
|
+
/**
|
|
40
|
+
* Whether a presented credential matches the required one.
|
|
41
|
+
*
|
|
42
|
+
* Compared with `timingSafeEqual` over the raw bytes, so the answer takes the same time whatever the
|
|
43
|
+
* first differing character is. The length check before it is unavoidable — `timingSafeEqual` throws
|
|
44
|
+
* on unequal lengths — and it does leak one bit: whether the presented credential is the right
|
|
45
|
+
* LENGTH.
|
|
46
|
+
*
|
|
47
|
+
* For a minted token that costs nothing: the length is fixed at 64 hex characters and published in
|
|
48
|
+
* this file. A host that supplies its own token of some other length leaks that length instead, and
|
|
49
|
+
* review was right to point out that the earlier wording ("fixed and public") quietly assumed the
|
|
50
|
+
* minted case for every case. Length is not the secret in either — but the claim now says which
|
|
51
|
+
* situation it is describing.
|
|
52
|
+
*
|
|
53
|
+
* A missing credential is a mismatch, not a special case: a peer that sends nothing and a peer that
|
|
54
|
+
* sends the wrong thing are equally unadmitted, and giving them different answers would tell an
|
|
55
|
+
* attacker which half they got right.
|
|
56
|
+
*/
|
|
57
|
+
declare function credentialMatches(expected: string, presented: string | undefined): boolean;
|
|
58
|
+
declare function bearerCredential(header: string | undefined | null): string | undefined;
|
|
59
|
+
//#endregion
|
|
60
|
+
//#region src/node/handoff-manifest.d.ts
|
|
61
|
+
/**
|
|
62
|
+
* Serialize a record and describe what was serialized.
|
|
63
|
+
*
|
|
64
|
+
* The string is returned alongside the integrity data because the digest is only meaningful for
|
|
65
|
+
* these exact bytes. A caller that re-serialized the record before sending could produce a
|
|
66
|
+
* different key order and a digest mismatch on a payload that is perfectly correct — a corruption
|
|
67
|
+
* report caused by the corruption check.
|
|
68
|
+
*/
|
|
69
|
+
declare function sealHandoffRecord(record: IInteractiveSessionRecord): {
|
|
70
|
+
serialized: string;
|
|
71
|
+
integrity: IHandoffIntegrity;
|
|
72
|
+
};
|
|
73
|
+
/** Why a received payload was not accepted. */
|
|
74
|
+
type TIntegrityFailure = 'truncated' | 'digest-mismatch';
|
|
75
|
+
interface IIntegrityVerdict {
|
|
76
|
+
readonly intact: boolean;
|
|
77
|
+
readonly failure?: TIntegrityFailure;
|
|
78
|
+
/** What the manifest promised and what arrived, so a report can say more than "it failed". */
|
|
79
|
+
readonly expectedBytes?: number;
|
|
80
|
+
readonly actualBytes?: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Check a received payload against the manifest's integrity metadata.
|
|
84
|
+
*
|
|
85
|
+
* Nothing is parsed here. Parsing a payload to check it is well-formed would mean building the
|
|
86
|
+
* object graph before knowing whether the bytes are the ones that were sent, and a destination that
|
|
87
|
+
* has already parsed a corrupt payload has already spent the effort this check exists to avoid.
|
|
88
|
+
*/
|
|
89
|
+
declare function verifyHandoffPayload(serialized: string, integrity: IHandoffIntegrity): IIntegrityVerdict;
|
|
90
|
+
//#endregion
|
|
91
|
+
//#region src/node/access-token-verifier.d.ts
|
|
92
|
+
/** What the verifier reaches outward through. Injected in tests; defaults to the real network. */
|
|
93
|
+
interface IAccessTokenVerifierDeps {
|
|
94
|
+
readonly fetch?: IEgressDeps['fetch'];
|
|
95
|
+
readonly lookup?: IEgressDeps['lookup'];
|
|
96
|
+
/** Wall-clock milliseconds since the epoch, for `exp`/`nbf`. Defaults to `Date.now`. */
|
|
97
|
+
readonly now?: () => number;
|
|
98
|
+
/** Monotonic milliseconds, for cache age and refetch intervals. Defaults to `performance.now`. */
|
|
99
|
+
readonly monotonicNow?: () => number;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Build a verifier for one issuer and one resource.
|
|
103
|
+
*
|
|
104
|
+
* @throws when the configuration cannot describe a safe verifier: a non-`https` issuer, no
|
|
105
|
+
* algorithm or an unsupported one, no required scope, or no allowlisted subject or client. The
|
|
106
|
+
* last is the single-tenant rule: without it any holder of a token from the issuer is admitted.
|
|
107
|
+
*/
|
|
108
|
+
declare function createAccessTokenVerifier(config: IAccessTokenVerifierConfig, deps?: IAccessTokenVerifierDeps): IAccessTokenVerifier;
|
|
109
|
+
//#endregion
|
|
110
|
+
//#region src/node/bearer-resource-gate.d.ts
|
|
111
|
+
/** A coarse class of a peer's address; the address itself is never reported. */
|
|
112
|
+
type TRemoteAddressClass = 'loopback' | 'private' | 'public' | 'unknown';
|
|
113
|
+
/** Where a refused request came from, and whether its address is over the failure budget. */
|
|
114
|
+
interface IBearerFailure {
|
|
115
|
+
readonly remote: TRemoteAddressClass;
|
|
116
|
+
readonly throttled: boolean;
|
|
117
|
+
readonly retryAfterSeconds: number;
|
|
118
|
+
}
|
|
119
|
+
/** The public-facing identity of one resource server and its per-address failure budget. */
|
|
120
|
+
interface IBearerResourceServer {
|
|
121
|
+
/** The parsed public URL; its path is the resource's base path. */
|
|
122
|
+
readonly url: URL;
|
|
123
|
+
/**
|
|
124
|
+
* Host and Origin must name the public URL. On a mismatch the 403 is written and false returned;
|
|
125
|
+
* nothing is counted, since no credential was judged.
|
|
126
|
+
*/
|
|
127
|
+
checkNames(req: IncomingMessage, res: ServerResponse): boolean;
|
|
128
|
+
/** Count one failure against the request's peer. */
|
|
129
|
+
fail(req: IncomingMessage): IBearerFailure;
|
|
130
|
+
/** The request's peer class without counting anything. */
|
|
131
|
+
remote(req: IncomingMessage): TRemoteAddressClass;
|
|
132
|
+
}
|
|
133
|
+
/** The challenges and metadata of one protected resource (one audience). */
|
|
134
|
+
interface IProtectedResource {
|
|
135
|
+
/** The RFC 9728 path clients fetch the metadata from. */
|
|
136
|
+
readonly wellKnownPath: string;
|
|
137
|
+
readonly metadata: string;
|
|
138
|
+
readonly challenges: {
|
|
139
|
+
readonly missing: string;
|
|
140
|
+
readonly invalid: string;
|
|
141
|
+
readonly scope: string;
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
/** Parse a resource server's public URL: plain `https`, no credentials, query or fragment. */
|
|
145
|
+
declare function parsePublicHttpsUrl(publicUrl: string, label: string): URL;
|
|
146
|
+
/**
|
|
147
|
+
* Build the public identity of a resource server. Throws when the public URL is not plain `https`,
|
|
148
|
+
* or a trusted proxy is not a literal address. `label` names the carrier in those messages.
|
|
149
|
+
*/
|
|
150
|
+
declare function createBearerResourceServer(options: {
|
|
151
|
+
readonly publicUrl: string;
|
|
152
|
+
/** Proxy addresses whose `X-Forwarded-For` is believed. Absent: the header is ignored. */
|
|
153
|
+
readonly trustedProxies?: readonly string[];
|
|
154
|
+
readonly label: string;
|
|
155
|
+
readonly now?: () => number;
|
|
156
|
+
}): IBearerResourceServer;
|
|
157
|
+
/**
|
|
158
|
+
* Describe one protected resource: where its RFC 9728 metadata lives and the challenges that point
|
|
159
|
+
* there. Throws for a non-`https` issuer, or no scope or a scope a challenge cannot quote.
|
|
160
|
+
*/
|
|
161
|
+
declare function describeProtectedResource(options: {
|
|
162
|
+
/** The resource identifier (`aud`), an `https` URL. */
|
|
163
|
+
readonly resource: string;
|
|
164
|
+
readonly issuer: string;
|
|
165
|
+
readonly scopes: readonly string[];
|
|
166
|
+
readonly label: string;
|
|
167
|
+
}): IProtectedResource;
|
|
168
|
+
/** Answer a metadata request: GET only, the JSON document, nothing else. */
|
|
169
|
+
declare function serveProtectedResourceMetadata(req: IncomingMessage, res: ServerResponse, resource: IProtectedResource): void;
|
|
170
|
+
/**
|
|
171
|
+
* Answer a refused bearer token after its failure was counted: 429 once the peer is over budget,
|
|
172
|
+
* 403 `insufficient_scope` for a missing scope, otherwise 401 with the missing-token or
|
|
173
|
+
* `invalid_token` challenge. Always an empty body.
|
|
174
|
+
*/
|
|
175
|
+
declare function refuseBearerToken(res: ServerResponse, resource: IProtectedResource, refusal: 'missing-token' | 'missing-scope' | 'invalid-token', failure: IBearerFailure): void;
|
|
176
|
+
/** Why a request was refused: a verifier refusal, or no bearer token at all. */
|
|
177
|
+
type TBearerRefusal = TAccessTokenRefusal | 'missing-token';
|
|
178
|
+
/** One refused request as an audit sink sees it. Carries no token text and no claim value. */
|
|
179
|
+
interface IBearerRefusalRecord {
|
|
180
|
+
readonly refusal: TBearerRefusal;
|
|
181
|
+
readonly remote: TRemoteAddressClass;
|
|
182
|
+
/** True when the refusal was answered 429 because the address exceeded its failure budget. */
|
|
183
|
+
readonly throttled: boolean;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Answer one refused request and report it. When the issuer's keys are unavailable the issuer, not
|
|
187
|
+
* the peer, failed: 503, not counted against the peer, and not a token verdict. Every other refusal
|
|
188
|
+
* is counted and answered with the matching challenge.
|
|
189
|
+
*/
|
|
190
|
+
declare function refuseAccessToken(req: IncomingMessage, res: ServerResponse, server: IBearerResourceServer, resource: IProtectedResource, refusal: TBearerRefusal, audit?: (record: IBearerRefusalRecord) => void): void;
|
|
191
|
+
//#endregion
|
|
192
|
+
//#region src/node/file-transfer.d.ts
|
|
193
|
+
/** Why a transfer did not end with the file kept. */
|
|
194
|
+
type TFileTransferRefusal =
|
|
195
|
+
/** The receiving operator said no, could not be asked, or the connection does not carry files. */
|
|
196
|
+
'declined' |
|
|
197
|
+
/** The file is over the size limit. */
|
|
198
|
+
'too-large' |
|
|
199
|
+
/** The name is empty, or would leave the receiving directory. */
|
|
200
|
+
'bad-name' |
|
|
201
|
+
/** A file of that name was already received; nothing is overwritten. */
|
|
202
|
+
'exists' |
|
|
203
|
+
/** The receiving directory is not one this user owns outright, e.g. a symbolic link. */
|
|
204
|
+
'unsafe-path' |
|
|
205
|
+
/** The content did not match the offered size or hash. The partial file is discarded. */
|
|
206
|
+
'integrity' |
|
|
207
|
+
/** A frame broke the protocol. */
|
|
208
|
+
'protocol' |
|
|
209
|
+
/** The channel ended first. */
|
|
210
|
+
'closed' |
|
|
211
|
+
/** The other side went quiet. */
|
|
212
|
+
'timeout' |
|
|
213
|
+
/** The receiver could not store the file. */
|
|
214
|
+
'unavailable';
|
|
215
|
+
/**
|
|
216
|
+
* The default size limit. A transfer does not resume, so a dropped connection costs the whole file
|
|
217
|
+
* again; a received file also sits on the receiver's disk until its operator removes it. The limit
|
|
218
|
+
* keeps both costs small while covering what is sent between one's own sessions: sources, logs,
|
|
219
|
+
* screenshots, a small archive.
|
|
220
|
+
*/
|
|
221
|
+
declare const DEFAULT_MAX_FILE_BYTES: number;
|
|
222
|
+
/**
|
|
223
|
+
* Bytes of content per chunk. Small enough that a frame, base64 and envelope included, stays well
|
|
224
|
+
* inside the message size every channel it runs on carries.
|
|
225
|
+
*/
|
|
226
|
+
declare const FILE_CHUNK_BYTES: number;
|
|
227
|
+
/** Chunks the sender may have unconfirmed. */
|
|
228
|
+
declare const FILE_CREDIT_WINDOW = 16;
|
|
229
|
+
/** Where content comes from. `read` yields the content once, in order, in pieces of any size. */
|
|
230
|
+
interface IFileSource {
|
|
231
|
+
readonly size: number;
|
|
232
|
+
read(): AsyncIterable<Uint8Array>;
|
|
233
|
+
}
|
|
234
|
+
/** Where received content goes until it is verified; nothing is kept unless `commit` runs. */
|
|
235
|
+
interface IFileSink {
|
|
236
|
+
write(chunk: Uint8Array): Promise<void>;
|
|
237
|
+
/** Keep the verified file. Resolves with where it is. */
|
|
238
|
+
commit(): Promise<string>;
|
|
239
|
+
/** Drop what was written. */
|
|
240
|
+
discard(): Promise<void>;
|
|
241
|
+
}
|
|
242
|
+
/** The receiver's decision on an offer: somewhere to put it, or why not. */
|
|
243
|
+
type TFileAdmission = {
|
|
244
|
+
readonly sink: IFileSink;
|
|
245
|
+
} | {
|
|
246
|
+
readonly refused: TFileTransferRefusal;
|
|
247
|
+
readonly detail?: string;
|
|
248
|
+
};
|
|
249
|
+
type TFileSendOutcome = {
|
|
250
|
+
readonly ok: true;
|
|
251
|
+
} | {
|
|
252
|
+
readonly ok: false;
|
|
253
|
+
readonly reason: TFileTransferRefusal;
|
|
254
|
+
readonly detail?: string;
|
|
255
|
+
};
|
|
256
|
+
type TFileReceiveOutcome = {
|
|
257
|
+
readonly ok: true;
|
|
258
|
+
readonly offer: IFileOffer;
|
|
259
|
+
readonly location: string;
|
|
260
|
+
} | {
|
|
261
|
+
readonly ok: false;
|
|
262
|
+
readonly reason: TFileTransferRefusal;
|
|
263
|
+
readonly detail?: string;
|
|
264
|
+
readonly offer?: IFileOffer;
|
|
265
|
+
};
|
|
266
|
+
interface ISendFileOptions {
|
|
267
|
+
readonly channel: IFileFrameChannel;
|
|
268
|
+
readonly offer: IFileOffer;
|
|
269
|
+
readonly source: IFileSource;
|
|
270
|
+
readonly chunkBytes?: number;
|
|
271
|
+
readonly window?: number;
|
|
272
|
+
readonly idleMs?: number;
|
|
273
|
+
/** How long the receiving operator may take to answer. */
|
|
274
|
+
readonly decisionMs?: number;
|
|
275
|
+
}
|
|
276
|
+
/**
|
|
277
|
+
* Offer a file and, once accepted, send it. Resolves when the receiver confirms it kept the file, or
|
|
278
|
+
* with why it did not. Closes the channel either way.
|
|
279
|
+
*/
|
|
280
|
+
declare function sendFileOverChannel(options: ISendFileOptions): Promise<TFileSendOutcome>;
|
|
281
|
+
interface IReceiveFileOptions {
|
|
282
|
+
readonly channel: IFileFrameChannel;
|
|
283
|
+
/** Offers above this are refused without asking anyone. */
|
|
284
|
+
readonly maxBytes?: number;
|
|
285
|
+
/**
|
|
286
|
+
* Decide on a well-formed offer within the size limit: ask whoever must approve it, and open
|
|
287
|
+
* somewhere to put it. `signal` aborts when the sender goes away.
|
|
288
|
+
*/
|
|
289
|
+
readonly admit: (offer: IFileOffer, signal: AbortSignal) => Promise<TFileAdmission>;
|
|
290
|
+
readonly window?: number;
|
|
291
|
+
readonly idleMs?: number;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Receive one file: the channel's first frame is the offer. Resolves with where the verified file was
|
|
295
|
+
* kept, or with why nothing was. Closes the channel either way.
|
|
296
|
+
*/
|
|
297
|
+
declare function receiveFileOverChannel(options: IReceiveFileOptions): Promise<TFileReceiveOutcome>;
|
|
298
|
+
//#endregion
|
|
299
|
+
//#region src/node/stdio-carrier.d.ts
|
|
300
|
+
interface IStdioCarrierHandlers {
|
|
301
|
+
/** The peer ended its side: `end` or `close` on the input. May fire more than once. */
|
|
302
|
+
readonly onInputClose: () => void;
|
|
303
|
+
/** The output closed. */
|
|
304
|
+
readonly onOutputClose: () => void;
|
|
305
|
+
/** The output failed to accept a write. */
|
|
306
|
+
readonly onOutputError: (error: Error) => void;
|
|
307
|
+
}
|
|
308
|
+
interface IStdioCarrier {
|
|
309
|
+
/** Hand this to the protocol SDK in place of the input; destroying it never destroys the input. */
|
|
310
|
+
readonly bridge: PassThrough;
|
|
311
|
+
/** Remove the listeners, unpipe and destroy the bridge. Safe to call again; the streams stay open. */
|
|
312
|
+
release(): void;
|
|
313
|
+
}
|
|
314
|
+
declare function attachStdioCarrier(input: Readable, output: Writable, handlers: IStdioCarrierHandlers): IStdioCarrier;
|
|
315
|
+
//#endregion
|
|
316
|
+
export { DEFAULT_MAX_FILE_BYTES, FILE_CHUNK_BYTES, FILE_CREDIT_WINDOW, type IAccessTokenVerifierDeps, type IBearerFailure, type IBearerRefusalRecord, type IBearerResourceServer, type IFileSink, type IFileSource, type IIntegrityVerdict, type IProtectedResource, type IReceiveFileOptions, type ISendFileOptions, type IStdioCarrier, type IStdioCarrierHandlers, type TBearerRefusal, type TFileAdmission, type TFileReceiveOutcome, type TFileSendOutcome, type TFileTransferRefusal, type TIntegrityFailure, type TRemoteAddressClass, attachStdioCarrier, bearerCredential, createAccessTokenVerifier, createBearerResourceServer, credentialMatches, describeProtectedResource, mintTransportToken, parsePublicHttpsUrl, receiveFileOverChannel, refuseAccessToken, refuseBearerToken, resolveAdmission, sealHandoffRecord, sendFileOverChannel, serveProtectedResourceMetadata, verifyHandoffPayload };
|
|
317
|
+
//# sourceMappingURL=node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node.d.ts","names":[],"sources":["../../src/node/admission.ts","../../src/node/handoff-manifest.ts","../../src/node/access-token-verifier.ts","../../src/node/bearer-resource-gate.ts","../../src/node/file-transfer.ts","../../src/node/stdio-carrier.ts"],"mappings":";;;;;;;;;;;;;iBA4BgB;;;;;;;;;;;;;;;;;;;;;;;;iBA+BA,iBACd,SAAQ,4BAA4B,sBACnC;;;;;;;;;;;;;;;;;;;iBAoDa,kBAAkB,kBAAkB;iBA8BpC,iBAAiB;;;;;;;;;;;iBCpHjB,kBAAkB,QAAQ;EACxC;EACA,WAAW;;;KAcD;UAEK;WACN;WACA,UAAU;;WAEV;WACA;;;;;;;;;iBAUK,qBACd,oBACA,WAAW,oBACV;;;;UCCc;WACN,QAAQ;WACR,SAAS;;WAET;;WAEA;;;;;;;;;iBAwBK,0BACd,QAAQ,4BACR,OAAM,2BACL;;;;KC9DS;;UAGK;WACN,QAAQ;WACR;WACA;;;UAIM;;WAEN,KAAK;;;;;EAKd,WAAW,KAAK,iBAAiB,KAAK;;EAEtC,KAAK,KAAK,kBAAkB;;EAE5B,OAAO,KAAK,kBAAkB;;;UAIf;;WAEN;WACA;WACA;aACE;aACA;aACA;;;;iBAkCG,oBAAoB,mBAAmB,gBAAgB;;;;;iBAoDvD,2BAA2B;WAChC;;WAEA;WACA;WACA;IACP;;;;;iBA0DY,0BAA0B;;WAE/B;WACA;WACA;WACA;IACP;;iBAoCY,+BACd,KAAK,iBACL,KAAK,gBACL,UAAU;;;;;;iBAcI,kBACd,KAAK,gBACL,UAAU,oBACV,8DACA,SAAS;;KAcC,iBAAiB;;UAGZ;WACN,SAAS;WACT,QAAQ;;WAER;;;;;;;iBAQK,kBACd,KAAK,iBACL,KAAK,gBACL,QAAQ,uBACR,UAAU,oBACV,SAAS,gBACT,SAAS,QAAQ;;;;KC7RP;;;;;;;;;;;;;;;;;;;;;;;;;;;cA4BC;;;;;cAMA;;cAEA;;UAcI;WACN;EACT,QAAQ,cAAc;;;UAIP;EACf,MAAM,OAAO,aAAa;;EAE1B,UAAU;;EAEV,WAAW;;;KAID;WACG,MAAM;;WACN,SAAS;WAA+B;;KAE3C;WACG;;WACA;WAAoB,QAAQ;WAA+B;;KAE9D;WACG;WAAmB,OAAO;WAAqB;;WAE/C;WACA,QAAQ;WACR;WACA,QAAQ;;UA4NN;WACN,SAAS;WACT,OAAO;WACP,QAAQ;WACR;WACA;WACA;;WAEA;;;;;;iBAOW,oBAAoB,SAAS,mBAAmB,QAAQ;UA2D7D;WACN,SAAS;;WAET;;;;;WAKA,QAAQ,OAAO,YAAY,QAAQ,gBAAgB,QAAQ;WAC3D;WACA;;;;;;iBAOW,uBACpB,SAAS,sBACR,QAAQ;;;UCrZM;;WAEN;;WAEA;;WAEA,gBAAgB,OAAO;;UAGjB;;WAEN,QAAQ;;EAEjB;;iBAGc,mBACd,OAAO,UACP,QAAQ,UACR,UAAU,wBACT"}
|