@agentshouse/kit 0.1.0-alpha.8 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/README.md +14 -1740
  2. package/dist/api.js +11 -0
  3. package/dist/home.js +24 -0
  4. package/dist/house-main.js +4 -0
  5. package/dist/kit-main.js +16 -0
  6. package/dist/login.js +112 -0
  7. package/dist/resident.js +10 -0
  8. package/dist/stream.js +31 -0
  9. package/package.json +16 -11
  10. package/bin/connect-linux.sh +0 -134
  11. package/bin/kit.js +0 -1023
  12. package/bootstrap.json +0 -17
  13. package/src/acp.js +0 -99
  14. package/src/adapter-mcp.js +0 -44
  15. package/src/adapter.js +0 -252
  16. package/src/agent-logins.js +0 -65
  17. package/src/agents.js +0 -504
  18. package/src/apply.js +0 -29
  19. package/src/attach.js +0 -52
  20. package/src/binding.js +0 -90
  21. package/src/blocks.js +0 -156
  22. package/src/bridge.js +0 -449
  23. package/src/bytes.js +0 -89
  24. package/src/channel.js +0 -37
  25. package/src/command.js +0 -109
  26. package/src/configuration.js +0 -465
  27. package/src/connector-login.js +0 -91
  28. package/src/connector.js +0 -107
  29. package/src/continuation.js +0 -61
  30. package/src/contract.js +0 -363
  31. package/src/conversation-originals.js +0 -143
  32. package/src/custom/authorization.js +0 -63
  33. package/src/declaration.js +0 -35
  34. package/src/delivery.js +0 -191
  35. package/src/directory.js +0 -102
  36. package/src/draft.js +0 -89
  37. package/src/google/calendar.js +0 -177
  38. package/src/google/declared.js +0 -146
  39. package/src/google/drive.js +0 -435
  40. package/src/google/gateway.js +0 -166
  41. package/src/google/gmail.js +0 -249
  42. package/src/google/id.js +0 -1
  43. package/src/google/oauth.js +0 -273
  44. package/src/google/source.js +0 -25
  45. package/src/hook.js +0 -376
  46. package/src/host.js +0 -121
  47. package/src/house-main.js +0 -79
  48. package/src/house.js +0 -1090
  49. package/src/inbound.js +0 -175
  50. package/src/instance/google.js +0 -1
  51. package/src/kit-work-stream.js +0 -84
  52. package/src/launcher-main.js +0 -19
  53. package/src/launcher.js +0 -276
  54. package/src/local-files.js +0 -113
  55. package/src/login.js +0 -274
  56. package/src/mcp.js +0 -71
  57. package/src/migrator.js +0 -248
  58. package/src/mount.js +0 -582
  59. package/src/operation.js +0 -20
  60. package/src/original.js +0 -631
  61. package/src/peer.js +0 -124
  62. package/src/plan.js +0 -57
  63. package/src/question.js +0 -316
  64. package/src/refusal.js +0 -79
  65. package/src/repository.js +0 -521
  66. package/src/session-bin/house +0 -2
  67. package/src/settlement.js +0 -57
  68. package/src/setup.js +0 -190
  69. package/src/source-originals.js +0 -116
  70. package/src/source.js +0 -286
  71. package/src/starter-set/grilling/LICENSE +0 -21
  72. package/src/starter-set/grilling/SKILL.md +0 -30
  73. package/src/starter-set/grilling/agents/openai.yaml +0 -3
  74. package/src/starter-set.js +0 -17
  75. package/src/state.js +0 -157
  76. package/src/sync-library.js +0 -293
  77. package/src/sync-state.js +0 -120
  78. package/src/sync.js +0 -482
  79. package/src/telegram.js +0 -1045
  80. package/src/terminal.js +0 -57
  81. package/src/tmux.js +0 -119
  82. package/src/worker.js +0 -925
  83. package/src/workspace.js +0 -500
package/src/original.js DELETED
@@ -1,631 +0,0 @@
1
- import { createHash } from "node:crypto";
2
- import { constants } from "node:fs";
3
- import { open } from "node:fs/promises";
4
- import { basename } from "node:path";
5
- import { transferBytes } from "./bytes.js";
6
- import {
7
- ATTACHMENT_REFERENCE,
8
- ATTACHMENT_TRANSFER_CONTRACT,
9
- BYTE_GRANT_POST_BYTES_MAX,
10
- ITEM_ATTACHMENTS_MAX,
11
- KIT_ATTACHMENTS_GET_PATH,
12
- KIT_ATTACHMENTS_UPLOAD_PATH,
13
- KIT_CAPTURE_ORIGINALS_SAVE_PATH,
14
- KIT_CONVERSATION_ORIGINALS_SAVE_PATH,
15
- ORIGINAL_MEDIA_TYPE_MAX,
16
- ORIGINAL_NAME_MAX,
17
- ORIGINAL_UUID,
18
- SHA256_DIGEST,
19
- } from "./contract.js";
20
- import { heldControl, houseRequest, maintainControl, openSession } from "./house.js";
21
- import { operationId } from "./operation.js";
22
- import { KitRefusal } from "./refusal.js";
23
- import { readAttachmentState, writeAttachmentState, writeText } from "./state.js";
24
-
25
- function refuse(code, detail = null) {
26
- throw new KitRefusal(code, detail);
27
- }
28
-
29
- function active(signal) {
30
- if (signal?.aborted === true) refuse("kit_command_cancelled");
31
- }
32
-
33
- function digestOf(bytes) {
34
- return createHash("sha256").update(bytes).digest("hex");
35
- }
36
-
37
- export function originalName(value) {
38
- if (typeof value !== "string" || value.length === 0 || value.length > ORIGINAL_NAME_MAX) {
39
- refuse("kit_original_name_invalid");
40
- }
41
- if (value.includes("/") || value.includes("\\") || value === "." || value === "..") {
42
- refuse("kit_original_name_invalid");
43
- }
44
- return value;
45
- }
46
-
47
- function originalUuid(value, code) {
48
- if (typeof value !== "string" || !ORIGINAL_UUID.test(value)) refuse(code);
49
- return value;
50
- }
51
-
52
- function originalMediaType(value) {
53
- if (value === undefined || value === null) return undefined;
54
- if (typeof value !== "string" || value.length === 0 || value.length > ORIGINAL_MEDIA_TYPE_MAX) {
55
- refuse("kit_original_media_type_invalid");
56
- }
57
- return value;
58
- }
59
-
60
- export function originalDescriptor(value) {
61
- if (
62
- !Number.isSafeInteger(value?.bytes) ||
63
- value.bytes < 0 ||
64
- value.bytes > BYTE_GRANT_POST_BYTES_MAX ||
65
- typeof value.sha256 !== "string" ||
66
- !SHA256_DIGEST.test(value.sha256)
67
- ) {
68
- refuse("kit_original_descriptor_invalid");
69
- }
70
- const mediaType = originalMediaType(value.media_type);
71
- return {
72
- bytes: value.bytes,
73
- sha256: value.sha256,
74
- ...(mediaType === undefined ? {} : { media_type: mediaType }),
75
- };
76
- }
77
-
78
- export function describedBytes(bytes) {
79
- if (bytes.length > BYTE_GRANT_POST_BYTES_MAX) refuse("original_bytes_exceeded");
80
- return { bytes: bytes.length, sha256: digestOf(bytes) };
81
- }
82
-
83
- function describes(body, descriptor) {
84
- return (
85
- Buffer.isBuffer(body) &&
86
- descriptor !== null &&
87
- body.length === descriptor.bytes &&
88
- digestOf(body) === descriptor.sha256
89
- );
90
- }
91
-
92
- function sessionDependencies(options, dependencies) {
93
- return {
94
- fetch: dependencies.fetch ?? options.fetch,
95
- now: dependencies.now ?? options.now,
96
- };
97
- }
98
-
99
- async function withSession(options, dependencies, operation) {
100
- const session = await openSession(options, sessionDependencies(options, dependencies));
101
- return maintainControl(session, (signal) => operation(session, signal), options.signal);
102
- }
103
-
104
- async function originalRequest(session, path, body, signal) {
105
- return houseRequest(
106
- session.house,
107
- await heldControl(session, signal),
108
- path,
109
- body,
110
- session.dependencies,
111
- signal,
112
- );
113
- }
114
-
115
- function outcomeOf(answer) {
116
- if (
117
- typeof answer?.original !== "string" ||
118
- typeof answer?.version !== "string" ||
119
- typeof answer?.save?.status !== "string"
120
- ) {
121
- refuse("kit_original_save_rejected");
122
- }
123
- return {
124
- original: answer.original,
125
- version: answer.version,
126
- save: {
127
- status: answer.save.status,
128
- failure: answer.save.failure ?? null,
129
- },
130
- upload: answer.upload,
131
- };
132
- }
133
-
134
- function grantOf(value, kind) {
135
- if (value === undefined) return null;
136
- if (typeof value?.url !== "string" || typeof value?.operation !== "string") {
137
- refuse("kit_original_grant_rejected", kind);
138
- }
139
- return { url: value.url, operation: value.operation };
140
- }
141
-
142
- function sendBytes(dependencies, grant, body, signal) {
143
- return transferBytes(
144
- { url: grant.url, method: "POST", operation: grant.operation, body, signal },
145
- dependencies,
146
- );
147
- }
148
-
149
- async function receiveBytes(session, grant, signal) {
150
- const transferred = await transferBytes(
151
- { url: grant.url, method: "GET", operation: grant.operation, signal },
152
- session.dependencies,
153
- );
154
- return transferred.bytes;
155
- }
156
-
157
- async function writeOutput(path, bytes, signal) {
158
- if (path === undefined) return;
159
- await writeText(path, bytes, signal);
160
- }
161
-
162
- const UNREADABLE = new Set(["ENOENT", "EACCES", "EISDIR", "ELOOP", "ENOTDIR"]);
163
-
164
- export async function readOriginalFile(path, expectedBytes = null) {
165
- let handle;
166
- try {
167
- handle = await open(path, constants.O_RDONLY | constants.O_NONBLOCK);
168
- } catch (error) {
169
- if (UNREADABLE.has(error?.code)) return null;
170
- throw error;
171
- }
172
- try {
173
- const state = await handle.stat();
174
- if (!state.isFile() || (expectedBytes !== null && state.size !== expectedBytes)) return null;
175
- if (state.size > BYTE_GRANT_POST_BYTES_MAX) refuse("original_bytes_exceeded");
176
- const bytes = await handle.readFile();
177
- if (bytes.length > BYTE_GRANT_POST_BYTES_MAX) refuse("original_bytes_exceeded");
178
- return bytes;
179
- } finally {
180
- await handle.close();
181
- }
182
- }
183
-
184
- async function publish(requested, body, accepted, dependencies, signal) {
185
- const answer = accepted(await requested());
186
- if (body === null || answer.save.status === "saved" || answer.upload === undefined) {
187
- return answer;
188
- }
189
- return accepted(await sendBytes(dependencies, grantOf(answer.upload, "upload"), body, signal));
190
- }
191
-
192
- function houseRequested(session, path, request, signal) {
193
- return () => originalRequest(session, path, request, signal);
194
- }
195
-
196
- function associated(answer, expected) {
197
- const outcome = outcomeOf(answer);
198
- originalUuid(outcome.original, "kit_original_association_rejected");
199
- originalUuid(outcome.version, "kit_original_association_rejected");
200
- for (const [field, value] of Object.entries(expected)) {
201
- if (answer[field] !== value) refuse("kit_original_association_rejected", field);
202
- }
203
- return {
204
- original: outcome.original,
205
- version: outcome.version,
206
- save: outcome.save,
207
- ...(outcome.upload === undefined ? {} : { upload: outcome.upload }),
208
- };
209
- }
210
-
211
- function capturedReference(options) {
212
- if (
213
- typeof options.room_ref !== "string" ||
214
- options.room_ref.length === 0 ||
215
- !Number.isInteger(options.position) ||
216
- options.position < 0 ||
217
- options.position >= ITEM_ATTACHMENTS_MAX
218
- ) {
219
- refuse("kit_original_association_invalid");
220
- }
221
- return {
222
- room_ref: options.room_ref,
223
- record: originalUuid(options.record, "kit_original_association_invalid"),
224
- position: options.position,
225
- accepted_version: originalUuid(options.accepted_version, "kit_original_version_invalid"),
226
- };
227
- }
228
-
229
- export async function saveCapturedOriginal(session, options) {
230
- active(options.signal);
231
- const reference = capturedReference(options);
232
- const descriptor = originalDescriptor(options.descriptor);
233
- const body = describes(options.body, descriptor) ? options.body : null;
234
- const expected = {
235
- record: reference.record,
236
- position: reference.position,
237
- accepted_version: reference.accepted_version,
238
- bytes: descriptor.bytes,
239
- sha256: descriptor.sha256,
240
- };
241
- const signal = options.signal ?? session.signal;
242
- const answered = await publish(
243
- houseRequested(
244
- session,
245
- KIT_CAPTURE_ORIGINALS_SAVE_PATH,
246
- body === null ? { ...reference, source_failure: "source_unavailable" } : reference,
247
- signal,
248
- ),
249
- body,
250
- (answer) => associated(answer, expected),
251
- session.dependencies,
252
- signal,
253
- );
254
- return { ...reference, original: answered.original, version: answered.version, save: answered.save };
255
- }
256
-
257
- export async function saveConversationOriginal(session, options) {
258
- active(options.signal);
259
- const attachment = originalUuid(options.attachment, "kit_original_association_invalid");
260
- const descriptor =
261
- options.descriptor === null || options.descriptor === undefined
262
- ? null
263
- : originalDescriptor(options.descriptor);
264
- const body = describes(options.body, descriptor) ? options.body : null;
265
- const expected = {
266
- direction: "incoming",
267
- ...(descriptor === null ? {} : { bytes: descriptor.bytes, sha256: descriptor.sha256 }),
268
- };
269
- const signal = options.signal ?? session.signal;
270
- const answered = await publish(
271
- houseRequested(
272
- session,
273
- KIT_CONVERSATION_ORIGINALS_SAVE_PATH,
274
- {
275
- attachment,
276
- ...(descriptor === null ? {} : { bytes: descriptor.bytes, sha256: descriptor.sha256 }),
277
- ...(body === null ? { source_failure: "source_unavailable" } : {}),
278
- },
279
- signal,
280
- ),
281
- body,
282
- (answer) => associated(answer, expected),
283
- session.dependencies,
284
- signal,
285
- );
286
- return { attachment, original: answered.original, version: answered.version, save: answered.save };
287
- }
288
-
289
- export function attachmentReference(value) {
290
- if (typeof value !== "string" || !ATTACHMENT_REFERENCE.test(value)) {
291
- refuse("kit_attachment_reference_invalid");
292
- }
293
- return value;
294
- }
295
-
296
- export function attachmentPlace(options) {
297
- const room = options.room;
298
- const privatePlace = options.private === true;
299
- if (room !== undefined && privatePlace) refuse("kit_attachment_place_ambiguous");
300
- if (room === undefined && !privatePlace) refuse("kit_attachment_place_missing");
301
- if (privatePlace) return {};
302
- if (typeof room !== "string" || room.length === 0) refuse("kit_attachment_place_missing");
303
- return { room_ref: room };
304
- }
305
-
306
- function uploadRequest(place, version, name, descriptor, mediaType) {
307
- return {
308
- ...place,
309
- version,
310
- name,
311
- bytes: descriptor.bytes,
312
- sha256: descriptor.sha256,
313
- ...(mediaType === undefined ? {} : { media_type: mediaType }),
314
- };
315
- }
316
-
317
- function attachmentOf(answer, expected) {
318
- if (
319
- typeof answer?.original !== "string" ||
320
- !ORIGINAL_UUID.test(answer.original) ||
321
- typeof answer.version !== "string" ||
322
- !ORIGINAL_UUID.test(answer.version) ||
323
- typeof answer.attachment !== "string" ||
324
- !ATTACHMENT_REFERENCE.test(answer.attachment) ||
325
- typeof answer.name !== "string" ||
326
- !Number.isSafeInteger(answer.bytes) ||
327
- typeof answer.sha256 !== "string" ||
328
- !SHA256_DIGEST.test(answer.sha256) ||
329
- typeof answer.save?.status !== "string"
330
- ) {
331
- refuse("kit_attachment_rejected");
332
- }
333
- for (const [field, value] of Object.entries(expected)) {
334
- if (answer[field] !== value) refuse("kit_attachment_association_rejected", field);
335
- }
336
- return {
337
- attachment: answer.attachment,
338
- original: answer.original,
339
- version: answer.version,
340
- name: answer.name,
341
- bytes: answer.bytes,
342
- sha256: answer.sha256,
343
- media_type: answer.media_type ?? null,
344
- save: { status: answer.save.status, failure: answer.save.failure ?? null },
345
- upload_failure: answer.upload_failure ?? null,
346
- upload: answer.upload,
347
- read: answer.read,
348
- download: answer.download,
349
- };
350
- }
351
-
352
- function uploadedExpectation(request) {
353
- return { version: request.version, name: request.name, bytes: request.bytes, sha256: request.sha256 };
354
- }
355
-
356
- function deliver(session, request, body, signal) {
357
- return publish(
358
- houseRequested(session, KIT_ATTACHMENTS_UPLOAD_PATH, request, signal),
359
- body,
360
- (answer) => attachmentOf(answer, uploadedExpectation(request)),
361
- session.dependencies,
362
- signal,
363
- );
364
- }
365
-
366
- const IMAGE_SIGNATURES = [
367
- { mediaType: "image/png", prefix: [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a] },
368
- { mediaType: "image/jpeg", prefix: [0xff, 0xd8, 0xff] },
369
- { mediaType: "image/gif", prefix: [0x47, 0x49, 0x46, 0x38] },
370
- {
371
- mediaType: "image/webp",
372
- prefix: [0x52, 0x49, 0x46, 0x46],
373
- suffix: { offset: 8, bytes: [0x57, 0x45, 0x42, 0x50] },
374
- },
375
- { mediaType: "image/bmp", prefix: [0x42, 0x4d] },
376
- ];
377
-
378
- function signed(header, offset, bytes) {
379
- return (
380
- header.length >= offset + bytes.length &&
381
- bytes.every((byte, index) => header[offset + index] === byte)
382
- );
383
- }
384
-
385
- function observedMediaType(header) {
386
- for (const signature of IMAGE_SIGNATURES) {
387
- if (!signed(header, 0, signature.prefix)) continue;
388
- if (signature.suffix !== undefined && !signed(header, signature.suffix.offset, signature.suffix.bytes)) {
389
- continue;
390
- }
391
- return signature.mediaType;
392
- }
393
- return null;
394
- }
395
-
396
- export async function localAttachment(path, version) {
397
- const body = await readOriginalFile(path);
398
- if (body === null) refuse("attachment_source_unreadable", path);
399
- const mediaType = observedMediaType(body);
400
- return {
401
- body,
402
- file: {
403
- version,
404
- name: originalName(basename(path)),
405
- ...describedBytes(body),
406
- ...(mediaType === null ? {} : { media_type: mediaType }),
407
- },
408
- };
409
- }
410
-
411
- export function uploadLocalAttachment(requested, local, dependencies, signal) {
412
- return publish(
413
- requested,
414
- local.body,
415
- (answer) => attachmentOf(answer, uploadedExpectation(local.file)),
416
- dependencies,
417
- signal,
418
- );
419
- }
420
-
421
- export function sendLocalConversationFile(requested, local, dependencies, signal) {
422
- const { version, bytes, sha256 } = local.file;
423
- return publish(
424
- requested,
425
- local.body,
426
- (answer) => associated(answer, { version, direction: "outgoing", bytes, sha256 }),
427
- dependencies,
428
- signal,
429
- );
430
- }
431
-
432
- function unreadable(answer) {
433
- const code = answer.save.status !== "saved" ? "attachment_read_not_saved" : "attachment_read_unavailable";
434
- const refusal = new KitRefusal(code, answer.read?.status ?? answer.save.status);
435
- refusal.payload = {
436
- attachment: answer.attachment,
437
- version: answer.version,
438
- save: answer.save,
439
- read: answer.read ?? null,
440
- };
441
- return refusal;
442
- }
443
-
444
- async function readAttachment(session, place, attachment, expected, signal) {
445
- const answer = attachmentOf(
446
- await originalRequest(
447
- session,
448
- KIT_ATTACHMENTS_GET_PATH,
449
- { ...place, attachment, download: true },
450
- signal,
451
- ),
452
- { ...expected, attachment },
453
- );
454
- if (answer.save.status !== "saved" || answer.read?.status !== "available") throw unreadable(answer);
455
- const grant = grantOf(answer.download, "download");
456
- if (grant === null) refuse("kit_attachment_grant_rejected", "download");
457
- const bytes = await receiveBytes(session, grant, signal);
458
- if (bytes.length !== answer.bytes || digestOf(bytes) !== answer.sha256) {
459
- refuse("attachment_bytes_mismatch");
460
- }
461
- return { answer, bytes };
462
- }
463
-
464
- function failure(error) {
465
- return error instanceof KitRefusal ? error.code : "kit_attachment_transfer_failed";
466
- }
467
-
468
- export async function placeAttachment(session, options) {
469
- active(options.signal);
470
- const place = options.room === undefined || options.room === null ? {} : { room_ref: options.room };
471
- const version = originalUuid(options.version, "kit_attachment_version_invalid");
472
- const name = originalName(options.name);
473
- const mediaType = originalMediaType(options.mediaType);
474
- if (!Buffer.isBuffer(options.body)) refuse("kit_attachment_file_missing");
475
- const request = uploadRequest(place, version, name, describedBytes(options.body), mediaType);
476
- const signal = options.signal ?? session.signal;
477
- const uploaded = await deliver(session, request, options.body, signal);
478
- const placed = {
479
- attachment: uploaded.attachment,
480
- original: uploaded.original,
481
- version: uploaded.version,
482
- name: uploaded.name,
483
- bytes: uploaded.bytes,
484
- sha256: uploaded.sha256,
485
- save: uploaded.save,
486
- };
487
- if (uploaded.save.status !== "saved") return placed;
488
- try {
489
- await readAttachment(session, place, uploaded.attachment, uploadedExpectation(request), signal);
490
- } catch (error) {
491
- placed.save = { status: "failed", failure: failure(error) };
492
- }
493
- return placed;
494
- }
495
-
496
- function stateKey(house, request) {
497
- return createHash("sha256")
498
- .update(
499
- JSON.stringify([
500
- house,
501
- request.room_ref ?? null,
502
- request.name,
503
- request.bytes,
504
- request.sha256,
505
- request.media_type ?? null,
506
- ]),
507
- )
508
- .digest("hex");
509
- }
510
-
511
- function persist(directory, key, house, request, answered, signal) {
512
- return writeAttachmentState(
513
- directory,
514
- key,
515
- {
516
- contract: ATTACHMENT_TRANSFER_CONTRACT,
517
- house,
518
- room_ref: request.room_ref ?? null,
519
- name: request.name,
520
- bytes: request.bytes,
521
- sha256: request.sha256,
522
- media_type: request.media_type ?? null,
523
- version: request.version,
524
- attachment: answered.attachment,
525
- save: answered.save,
526
- },
527
- signal,
528
- );
529
- }
530
-
531
- function heldVersion(held) {
532
- return typeof held?.version === "string" && ORIGINAL_UUID.test(held.version)
533
- ? held.version
534
- : operationId();
535
- }
536
-
537
- export async function uploadAttachment(options, dependencies = {}) {
538
- active(options.signal);
539
- const place = attachmentPlace(options);
540
- if (typeof options.file !== "string" || options.file.length === 0) {
541
- refuse("kit_attachment_file_missing");
542
- }
543
- const name = originalName(options.name ?? basename(options.file));
544
- const mediaType = originalMediaType(options.mediaType);
545
- const body = await readOriginalFile(options.file);
546
- if (body === null) refuse("attachment_source_unreadable");
547
- const descriptor = describedBytes(body);
548
- const provisional = uploadRequest(place, null, name, descriptor, mediaType);
549
- const key = stateKey(options.house, provisional);
550
- const held = await readAttachmentState(options.directory, key);
551
- const request = { ...provisional, version: heldVersion(held) };
552
- await persist(
553
- options.directory,
554
- key,
555
- options.house,
556
- request,
557
- {
558
- attachment: held?.version === request.version ? held.attachment ?? null : null,
559
- save: { status: "pending", failure: null },
560
- },
561
- options.signal,
562
- );
563
- return withSession(options, dependencies, async (session, signal) => {
564
- const uploaded = await deliver(session, request, body, signal);
565
- await persist(options.directory, key, options.house, request, uploaded, signal);
566
- if (uploaded.save.status !== "saved") {
567
- const refusal = new KitRefusal(
568
- "attachment_upload_failed",
569
- uploaded.upload_failure ?? uploaded.save.failure ?? uploaded.save.status,
570
- );
571
- refusal.payload = {
572
- attachment: uploaded.attachment,
573
- version: uploaded.version,
574
- save: uploaded.save,
575
- };
576
- throw refusal;
577
- }
578
- const read = await readAttachment(
579
- session,
580
- place,
581
- uploaded.attachment,
582
- uploadedExpectation(request),
583
- signal,
584
- );
585
- await writeOutput(options.output, read.bytes, signal);
586
- return {
587
- attachment: uploaded.attachment,
588
- original: uploaded.original,
589
- version: uploaded.version,
590
- name: uploaded.name,
591
- save: uploaded.save,
592
- read: { status: "available" },
593
- bytes: read.bytes.length,
594
- sha256: digestOf(read.bytes),
595
- };
596
- });
597
- }
598
-
599
- export async function getAttachment(options, dependencies = {}) {
600
- active(options.signal);
601
- const place = attachmentPlace(options);
602
- const attachment = attachmentReference(options.attachment);
603
- return withSession(options, dependencies, async (session, signal) => {
604
- const read = await readAttachment(session, place, attachment, {}, signal);
605
- await writeOutput(options.output, read.bytes, signal);
606
- return {
607
- attachment: read.answer.attachment,
608
- original: read.answer.original,
609
- version: read.answer.version,
610
- name: read.answer.name,
611
- media_type: read.answer.media_type,
612
- save: read.answer.save,
613
- read: read.answer.read,
614
- bytes: read.bytes.length,
615
- sha256: digestOf(read.bytes),
616
- };
617
- });
618
- }
619
-
620
- export function formatAttachmentUpload(result) {
621
- return [
622
- `Uploaded attachment ${result.attachment} as ${result.name} (${result.save.status}).`,
623
- `Read ${result.bytes} bytes sha256=${result.sha256}.`,
624
- ];
625
- }
626
-
627
- export function formatAttachmentGet(result) {
628
- return [
629
- `Read attachment ${result.attachment} (${result.name}) ${result.bytes} bytes sha256=${result.sha256}.`,
630
- ];
631
- }