@songsid/agend 2.3.1 → 2.3.2-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authority/authority-state.d.ts +4 -0
- package/dist/authority/authority-state.js +16 -10
- package/dist/authority/authority-state.js.map +1 -1
- package/dist/authority/session-authorizations.d.ts +7 -1
- package/dist/authority/session-authorizations.js +6 -4
- package/dist/authority/session-authorizations.js.map +1 -1
- package/dist/authority/web-authority.d.ts +19 -6
- package/dist/authority/web-authority.js +22 -8
- package/dist/authority/web-authority.js.map +1 -1
- package/dist/channel/adapters/discord.d.ts +2 -0
- package/dist/channel/adapters/discord.js +33 -3
- package/dist/channel/adapters/discord.js.map +1 -1
- package/dist/channel/mcp-tools.js +5 -5
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/types.d.ts +11 -0
- package/dist/config-validator.js +40 -3
- package/dist/config-validator.js.map +1 -1
- package/dist/daemon.d.ts +34 -3
- package/dist/daemon.js +227 -46
- package/dist/daemon.js.map +1 -1
- package/dist/delivery-outbox.d.ts +12 -1
- package/dist/delivery-outbox.js +67 -15
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/doctor.d.ts +3 -0
- package/dist/doctor.js +9 -0
- package/dist/doctor.js.map +1 -1
- package/dist/event-log.d.ts +5 -0
- package/dist/event-log.js +13 -0
- package/dist/event-log.js.map +1 -1
- package/dist/fleet-manager.d.ts +140 -0
- package/dist/fleet-manager.js +414 -29
- package/dist/fleet-manager.js.map +1 -1
- package/dist/fleet-process-handlers.d.ts +1 -0
- package/dist/fleet-process-handlers.js +9 -1
- package/dist/fleet-process-handlers.js.map +1 -1
- package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +4 -4
- package/dist/general-knowledge/skills/web-appearance/SKILL.md +7 -7
- package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +106 -55
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +107 -56
- package/dist/guest-seen-people.d.ts +38 -0
- package/dist/guest-seen-people.js +78 -0
- package/dist/guest-seen-people.js.map +1 -0
- package/dist/instance-config-impact.js +4 -1
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +1 -0
- package/dist/instance-lifecycle.js +16 -1
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/locale.js +38 -10
- package/dist/locale.js.map +1 -1
- package/dist/mac-auto-restore.d.ts +117 -0
- package/dist/mac-auto-restore.js +294 -0
- package/dist/mac-auto-restore.js.map +1 -0
- package/dist/outbound-schemas.d.ts +2 -0
- package/dist/outbound-schemas.js +2 -0
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-web-gateway.js +9 -1
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/settings-api.d.ts +26 -0
- package/dist/settings-api.js +107 -1
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +37 -3
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +5 -2
- package/dist/settings-effect.js.map +1 -1
- package/dist/start-attempt.d.ts +9 -5
- package/dist/start-attempt.js +27 -8
- package/dist/start-attempt.js.map +1 -1
- package/dist/start-failure-chat.d.ts +4 -0
- package/dist/start-failure-chat.js +23 -6
- package/dist/start-failure-chat.js.map +1 -1
- package/dist/start-failure-notice.d.ts +4 -0
- package/dist/start-failure-notice.js +8 -0
- package/dist/start-failure-notice.js.map +1 -1
- package/dist/start-failure.d.ts +25 -0
- package/dist/start-failure.js +75 -2
- package/dist/start-failure.js.map +1 -1
- package/dist/turn-reply-guard.d.ts +25 -0
- package/dist/turn-reply-guard.js +111 -12
- package/dist/turn-reply-guard.js.map +1 -1
- package/dist/types.d.ts +18 -1
- package/dist/ui/backend-signin.js +5 -2
- package/dist/ui/chat-ambient.js +1 -1
- package/dist/ui/chat-effects.js +4 -4
- package/dist/ui/chat-render.js +5 -2
- package/dist/ui/chat-strings.js +2 -2
- package/dist/ui/chat-thread.js +2 -2
- package/dist/ui/first-run.js +2 -2
- package/dist/ui/fleet-canvas-view.js +114 -0
- package/dist/ui/fleet-canvas.js +15 -77
- package/dist/ui/fleet-office-editor.js +80 -5
- package/dist/ui/fleet-office-layout.js +197 -0
- package/dist/ui/fleet-office-model.js +6 -3
- package/dist/ui/fleet-office-pack.d.ts +39 -0
- package/dist/ui/fleet-office-pack.js +240 -29
- package/dist/ui/fleet-office-skin.js +130 -21
- package/dist/ui/fleet-office-store.js +75 -4
- package/dist/ui/fleet-office-view.js +154 -0
- package/dist/ui/fleet-office-zones.js +95 -33
- package/dist/ui/fleet-office.js +530 -94
- package/dist/ui/fleet-usage.js +7 -6
- package/dist/ui/panel-chat.js +1 -1
- package/dist/ui/panel-details.js +2 -2
- package/dist/ui/panel-fleet.js +11 -77
- package/dist/ui/panel-log.js +2 -2
- package/dist/ui/panel-settings.js +578 -163
- package/dist/ui/search-dialog.js +26 -7
- package/dist/ui/settings-appearance.js +82 -29
- package/dist/ui/settings-apply.js +42 -7
- package/dist/ui/settings-dialogs.js +68 -22
- package/dist/ui/settings-draft.js +51 -0
- package/dist/ui/settings-guests.js +244 -78
- package/dist/ui/settings-learn.js +2 -2
- package/dist/ui/settings-model.js +2 -0
- package/dist/ui/settings-registry.js +142 -0
- package/dist/ui/settings-strings.js +102 -44
- package/dist/ui/settings-web-authority.js +34 -23
- package/dist/ui/shared/app-i18n.js +14 -0
- package/dist/ui/shared/app-nav.js +10 -5
- package/dist/ui/shared/app-prefs.js +80 -2
- package/dist/ui/shared/app-route.js +39 -5
- package/dist/ui/shared/app-shell.js +59 -4
- package/dist/ui/shared/app-stream.js +2 -1
- package/dist/ui/shared/app.css +242 -17
- package/dist/ui/shared/app.js +3 -0
- package/dist/ui/shared/effect-stock-trust.js +1 -1
- package/dist/ui/shared/identity-color.js +4 -4
- package/dist/ui/shared/signin.js +2 -2
- package/dist/ui/shared/ui-dialog.js +6 -1
- package/dist/ui/shared/ui-icons.js +2 -0
- package/dist/ui/shared/web-authorize.js +13 -12
- package/dist/web-api.d.ts +8 -2
- package/dist/web-api.js +160 -5
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.js +7 -1
- package/dist/web-auth.js.map +1 -1
- package/dist/web-experimental.d.ts +1 -1
- package/dist/web-experimental.js +1 -1
- package/dist/web-experimental.js.map +1 -1
- package/dist/web-mod-agent.js +2 -2
- package/dist/web-mod-agent.js.map +1 -1
- package/dist/web-office-skins.d.ts +108 -0
- package/dist/web-office-skins.js +298 -0
- package/dist/web-office-skins.js.map +1 -0
- package/dist/web-office-store.d.ts +8 -2
- package/dist/web-office-store.js +37 -7
- package/dist/web-office-store.js.map +1 -1
- package/dist/web-settings-patch.d.ts +6 -0
- package/dist/web-settings-patch.js +61 -4
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/web-shell-routes.d.ts +4 -2
- package/dist/web-shell-routes.js +17 -7
- package/dist/web-shell-routes.js.map +1 -1
- package/dist/working-recovery.d.ts +104 -0
- package/dist/working-recovery.js +263 -0
- package/dist/working-recovery.js.map +1 -0
- package/dist/working-state-journal.d.ts +3 -1
- package/dist/working-state-journal.js +15 -2
- package/dist/working-state-journal.js.map +1 -1
- package/package.json +1 -1
package/dist/web-api.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ import type { ModStore } from "./web-mod-store.js";
|
|
|
9
9
|
import type { WebEffectService } from "./web-effect-service.js";
|
|
10
10
|
import type { ThemeStore } from "./web-theme-store.js";
|
|
11
11
|
import type { OfficeStore, OfficeWriteLimiter } from "./web-office-store.js";
|
|
12
|
+
import { type OfficeSkinStore } from "./web-office-skins.js";
|
|
12
13
|
import type { WebAppearance } from "./web-appearance.js";
|
|
13
14
|
import type { SendOpts } from "./channel/types.js";
|
|
14
15
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
@@ -195,6 +196,10 @@ export interface WebApiContext {
|
|
|
195
196
|
readonly officeStore?: OfficeStore;
|
|
196
197
|
/** How often a person may save the offices (absent: no limit, as in a test without it). */
|
|
197
198
|
readonly officeWrites?: OfficeWriteLimiter;
|
|
199
|
+
/** #2513: the Office view's uploaded skin packs (web-office-skins/); absent: /ui/office-skins is not offered. */
|
|
200
|
+
readonly officeSkins?: OfficeSkinStore;
|
|
201
|
+
/** How often a person may upload or delete a skin pack (absent: no limit). */
|
|
202
|
+
readonly officeSkinWrites?: OfficeWriteLimiter;
|
|
198
203
|
readonly webAppearance?: WebAppearance;
|
|
199
204
|
/** #1629 r4-5: installed mods and a person's staged import. */
|
|
200
205
|
readonly mods?: ModStore;
|
|
@@ -402,8 +407,9 @@ export interface WebPerson {
|
|
|
402
407
|
readonly platformName?: string;
|
|
403
408
|
}
|
|
404
409
|
/**
|
|
405
|
-
* #1939 §3.4 / #2453 §0.2: where this request came from — the
|
|
406
|
-
*
|
|
410
|
+
* #1939 §3.4 / #2453 §0.2 / §0.3: where this request came from — over the public gateway, the link itself by the
|
|
411
|
+
* gateway's own provenance (never a header): the admin's named tunnel on its exact hostname, else the temporary
|
|
412
|
+
* quick-tunnel link. Off the gateway, what the TCP peer and Host say.
|
|
407
413
|
*/
|
|
408
414
|
export declare function webPeerOf(req: IncomingMessage): WebPeer;
|
|
409
415
|
export declare function handleWebRequest(req: IncomingMessage, res: ServerResponse, url: URL, ctx: WebApiContext): boolean;
|
package/dist/web-api.js
CHANGED
|
@@ -15,6 +15,7 @@ import { AGENT_WIDGET_SLOTS, checkWidgetData, checkWidgetSpec } from "./web-widg
|
|
|
15
15
|
import { checkOwnerCss, ownerCssAssetRefs } from "./web-owner-css.js";
|
|
16
16
|
import { canonicalText } from "./web-theme.js";
|
|
17
17
|
import { EFFECT_WIRE_LIMIT } from "./web-effect-upload.js";
|
|
18
|
+
import { SKIN_WIRE_BYTES } from "./web-office-skins.js";
|
|
18
19
|
import { gatewayRequestContext } from "./web-request-context.js";
|
|
19
20
|
import { crossInstanceVisibility } from "./cross-instance-notice.js";
|
|
20
21
|
import { permitWebContinuation } from "./web-continuation.js";
|
|
@@ -447,6 +448,141 @@ function handleAppIcon(req, res, url, ctx, method) {
|
|
|
447
448
|
});
|
|
448
449
|
return true;
|
|
449
450
|
}
|
|
451
|
+
/**
|
|
452
|
+
* #2513: the Office view's skin packs (web-office-skins.ts). The reads are the offices' (GET /ui/offices): whoever may
|
|
453
|
+
* open the org chart, the public link too (its manifest lists them; a guest's routes do not): the list, a pack's
|
|
454
|
+
* manifest (?v=<sha>: immutable, else no-store) and its pictures (by the sha of their bytes: immutable, image/png,
|
|
455
|
+
* nosniff, sandboxed). The writes are PUT /ui/offices's: an admin session only — never the CLI token, never the public
|
|
456
|
+
* link (absent from its manifest, and refused here too) — asked again once the body is in; rate limited per person.
|
|
457
|
+
* Every page hears `office_skins` on the stream and reads the list again.
|
|
458
|
+
*/
|
|
459
|
+
function handleOfficeSkins(req, res, url, ctx, path, method) {
|
|
460
|
+
const store = ctx.officeSkins;
|
|
461
|
+
if (!store) {
|
|
462
|
+
json(res, 404, { error: "Skin packs are not kept on this fleet" });
|
|
463
|
+
return true;
|
|
464
|
+
}
|
|
465
|
+
const one = /^\/ui\/office-skins\/([A-Za-z0-9_-]{1,40})$/.exec(path);
|
|
466
|
+
const picture = /^\/ui\/office-skins\/([A-Za-z0-9_-]{1,40})\/img\/([0-9a-f]{32})\.png$/.exec(path);
|
|
467
|
+
if (method === "GET" || method === "HEAD") {
|
|
468
|
+
res.setHeader("X-Content-Type-Options", "nosniff");
|
|
469
|
+
if (path === "/ui/office-skins") {
|
|
470
|
+
res.setHeader("Cache-Control", "private, no-store");
|
|
471
|
+
json(res, 200, store.list());
|
|
472
|
+
return true;
|
|
473
|
+
}
|
|
474
|
+
if (one) {
|
|
475
|
+
const pack = store.get(one[1]);
|
|
476
|
+
if (!pack) {
|
|
477
|
+
res.setHeader("Cache-Control", "private, no-store");
|
|
478
|
+
json(res, 404, { error: "skin_unknown" });
|
|
479
|
+
return true;
|
|
480
|
+
}
|
|
481
|
+
const v = url.searchParams.get("v");
|
|
482
|
+
res.setHeader("Cache-Control", v === pack.sha ? "private, max-age=31536000, immutable" : "private, no-store");
|
|
483
|
+
json(res, 200, pack);
|
|
484
|
+
return true;
|
|
485
|
+
}
|
|
486
|
+
if (picture) {
|
|
487
|
+
const bytes = store.picture(picture[1], picture[2]);
|
|
488
|
+
if (!bytes) {
|
|
489
|
+
res.setHeader("Cache-Control", "private, no-store");
|
|
490
|
+
json(res, 404, { error: "No such image" });
|
|
491
|
+
return true;
|
|
492
|
+
}
|
|
493
|
+
res.setHeader("Content-Type", "image/png");
|
|
494
|
+
// Keyed by the sha of its bytes: a picture at this URL never changes.
|
|
495
|
+
res.setHeader("Cache-Control", "private, max-age=31536000, immutable");
|
|
496
|
+
res.setHeader("Content-Security-Policy", "default-src 'none'; sandbox");
|
|
497
|
+
res.writeHead(200);
|
|
498
|
+
res.end(method === "HEAD" ? undefined : bytes);
|
|
499
|
+
return true;
|
|
500
|
+
}
|
|
501
|
+
json(res, 404, { error: "not found" });
|
|
502
|
+
return true;
|
|
503
|
+
}
|
|
504
|
+
const upload = method === "POST" && path === "/ui/office-skins", removal = method === "DELETE" && !!one;
|
|
505
|
+
if (!upload && !removal) {
|
|
506
|
+
json(res, 404, { error: "not found" });
|
|
507
|
+
return true;
|
|
508
|
+
}
|
|
509
|
+
if (gatewayRequestContext(req)) {
|
|
510
|
+
json(res, 403, { error: "skins_not_over_public_link" });
|
|
511
|
+
return true;
|
|
512
|
+
}
|
|
513
|
+
const who = () => {
|
|
514
|
+
const verdict = evaluateWebRequest(req, url, ctx.webToken ?? null, ctx.webSessions, { touch: false });
|
|
515
|
+
if (verdict.kind !== "allow")
|
|
516
|
+
return { status: 401 };
|
|
517
|
+
return verdict.via === "session" && verdict.session?.tier === "admin" ? { person: verdict.session.owner ? webPersonKey(verdict.session.owner) : "admin-session" } : { status: 403 };
|
|
518
|
+
};
|
|
519
|
+
const refuse = (status) => json(res, status, { error: status === 401 ? WEB_TOKEN_INVALID_MESSAGE : "skins_need_an_admin_session" });
|
|
520
|
+
const first = who();
|
|
521
|
+
if ("status" in first) {
|
|
522
|
+
refuse(first.status);
|
|
523
|
+
return true;
|
|
524
|
+
}
|
|
525
|
+
const limited = (person) => {
|
|
526
|
+
const allowed = ctx.officeSkinWrites ? ctx.officeSkinWrites.check(person) : { ok: true };
|
|
527
|
+
if (allowed.ok)
|
|
528
|
+
return false;
|
|
529
|
+
res.setHeader("Retry-After", String(allowed.retry_after_s));
|
|
530
|
+
json(res, 429, { error: "skins_rate_limited", retry_after_s: allowed.retry_after_s });
|
|
531
|
+
return true;
|
|
532
|
+
};
|
|
533
|
+
const answer = (r, body, person) => {
|
|
534
|
+
if (!r.ok) {
|
|
535
|
+
json(res, r.status, { error: r.error, ...(r.detail ? { detail: r.detail } : {}) });
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
ctx.officeSkinWrites?.record(person);
|
|
539
|
+
ctx.emitSseEvent("office_skins", { rev: r.rev });
|
|
540
|
+
json(res, 200, body());
|
|
541
|
+
};
|
|
542
|
+
if (removal) {
|
|
543
|
+
if (limited(first.person))
|
|
544
|
+
return true;
|
|
545
|
+
const r = store.remove(one[1]);
|
|
546
|
+
answer(r, () => ({ rev: r.ok ? r.rev : "" }), first.person);
|
|
547
|
+
return true;
|
|
548
|
+
}
|
|
549
|
+
(async () => {
|
|
550
|
+
let bytes;
|
|
551
|
+
try {
|
|
552
|
+
bytes = await readBoundedWebBody(req, SKIN_WIRE_BYTES);
|
|
553
|
+
}
|
|
554
|
+
catch {
|
|
555
|
+
json(res, 413, { error: "skin_too_large", detail: `at most ${Math.floor(SKIN_WIRE_BYTES / 1024 / 1024)} MiB a request` });
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
if (!permitWebContinuation(req, res, ctx))
|
|
559
|
+
return;
|
|
560
|
+
const now = who();
|
|
561
|
+
if ("status" in now) {
|
|
562
|
+
refuse(now.status);
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
let body;
|
|
566
|
+
try {
|
|
567
|
+
body = JSON.parse(bytes.toString("utf8"));
|
|
568
|
+
}
|
|
569
|
+
catch {
|
|
570
|
+
json(res, 400, { error: "Invalid JSON" });
|
|
571
|
+
return;
|
|
572
|
+
}
|
|
573
|
+
if (limited(now.person))
|
|
574
|
+
return;
|
|
575
|
+
const r = store.add(body, now.person);
|
|
576
|
+
answer(r, () => (r.ok ? { rev: r.rev, pack: r.pack } : null), now.person);
|
|
577
|
+
})().catch(err => {
|
|
578
|
+
ctx.logger.error({ err: err.message }, "Uploading a skin pack failed");
|
|
579
|
+
try {
|
|
580
|
+
json(res, 500, { error: "Uploading a skin pack failed" });
|
|
581
|
+
}
|
|
582
|
+
catch { /* already answered */ }
|
|
583
|
+
});
|
|
584
|
+
return true;
|
|
585
|
+
}
|
|
450
586
|
/**
|
|
451
587
|
* #1629 r4 §3: the owner-CSS routes (see their call site). `who()`, asked first and again after any body: the person a
|
|
452
588
|
* signed-in human session is, or the status to answer.
|
|
@@ -1311,11 +1447,13 @@ function webAuthorityOf(req, url, ctx, instance) {
|
|
|
1311
1447
|
peer: webPeerOf(req), ...(instance !== undefined ? { instance } : {}) });
|
|
1312
1448
|
}
|
|
1313
1449
|
/**
|
|
1314
|
-
* #1939 §3.4 / #2453 §0.2: where this request came from — the
|
|
1315
|
-
*
|
|
1450
|
+
* #1939 §3.4 / #2453 §0.2 / §0.3: where this request came from — over the public gateway, the link itself by the
|
|
1451
|
+
* gateway's own provenance (never a header): the admin's named tunnel on its exact hostname, else the temporary
|
|
1452
|
+
* quick-tunnel link. Off the gateway, what the TCP peer and Host say.
|
|
1316
1453
|
*/
|
|
1317
1454
|
export function webPeerOf(req) {
|
|
1318
|
-
|
|
1455
|
+
const gateway = gatewayRequestContext(req);
|
|
1456
|
+
return gateway ? (gateway.named === true ? "named_link" : "quick_link") : classifyWebPeer(req);
|
|
1319
1457
|
}
|
|
1320
1458
|
/**
|
|
1321
1459
|
* #2122: what let this request in, asked now — its session (by handle, under the token it was signed in with) or the
|
|
@@ -2074,6 +2212,8 @@ export function handleWebRequest(req, res, url, ctx) {
|
|
|
2074
2212
|
});
|
|
2075
2213
|
return true;
|
|
2076
2214
|
}
|
|
2215
|
+
if (path === "/ui/office-skins" || path.startsWith("/ui/office-skins/"))
|
|
2216
|
+
return handleOfficeSkins(req, res, url, ctx, path, method);
|
|
2077
2217
|
// ── #1630: avatars and Discord server emoji, same-origin (the page never loads a platform's CDN; img-src stays 'self') ──
|
|
2078
2218
|
const imageMatch = path.match(/^\/ui\/img\/(bot|avatar|emoji)\/([^/]+)$/);
|
|
2079
2219
|
if (method === "GET" && imageMatch) {
|
|
@@ -3268,10 +3408,10 @@ export function handleWebRequest(req, res, url, ctx) {
|
|
|
3268
3408
|
return [403, "usage_report_not_over_public_link"];
|
|
3269
3409
|
return null;
|
|
3270
3410
|
};
|
|
3271
|
-
// Over the public link, where to turn it on: Settings → Web on the dashboard reached directly (this computer's own
|
|
3411
|
+
// Over the public link, where to turn it on: Settings → Web dashboard on the dashboard reached directly (this computer's own
|
|
3272
3412
|
// address — the public link's Settings only reads). Never the public origin's own Settings, which cannot change it.
|
|
3273
3413
|
const answer = ([status, error]) => json(res, status, {
|
|
3274
|
-
error, ...(status === 403 ? { settings: `http://localhost:${ctx.fleetConfig?.health_port ?? 19280}/settings/web` } : {})
|
|
3414
|
+
error, ...(status === 403 ? { settings: `http://localhost:${ctx.fleetConfig?.health_port ?? 19280}/settings/web-dashboard` } : {})
|
|
3275
3415
|
});
|
|
3276
3416
|
const first = refusal();
|
|
3277
3417
|
if (first) {
|
|
@@ -3866,6 +4006,11 @@ function handleUpload(req, res, url, ctx) {
|
|
|
3866
4006
|
// staging a file never wakes it. A stopped or deleted recipient must not regain an upload id.
|
|
3867
4007
|
const deliverable = () => uploadRecipient(ctx, instance);
|
|
3868
4008
|
const gone = () => drop(() => json(res, 404, { error: `Instance not found: ${instance}` }));
|
|
4009
|
+
// #2366: a guest's scope (and whether guests may chat) is asked again at each commit point — narrowed, revoked or made
|
|
4010
|
+
// read-only while the body was arriving, the upload is refused and its part file removed, as it would have been at
|
|
4011
|
+
// the start. Read now: the scope is never stored (guestRequestOf's scope() is the fleet's guestScope as it is now).
|
|
4012
|
+
const guestNow = () => guestChatRefusal(guestRequestOf(req), instance);
|
|
4013
|
+
const refuseGuest = (r) => drop(() => json(res, r.status, { error: r.error }));
|
|
3869
4014
|
req.on("end", () => {
|
|
3870
4015
|
if (over)
|
|
3871
4016
|
return;
|
|
@@ -3880,6 +4025,11 @@ function handleUpload(req, res, url, ctx) {
|
|
|
3880
4025
|
drop(() => json(res, 415, { error: "the file is empty" }));
|
|
3881
4026
|
return;
|
|
3882
4027
|
}
|
|
4028
|
+
const refusedAtEnd = guestNow();
|
|
4029
|
+
if (refusedAtEnd) {
|
|
4030
|
+
refuseGuest(refusedAtEnd);
|
|
4031
|
+
return;
|
|
4032
|
+
}
|
|
3883
4033
|
if (!deliverable()) {
|
|
3884
4034
|
gone();
|
|
3885
4035
|
return;
|
|
@@ -3892,6 +4042,11 @@ function handleUpload(req, res, url, ctx) {
|
|
|
3892
4042
|
drop();
|
|
3893
4043
|
return;
|
|
3894
4044
|
}
|
|
4045
|
+
const refusedAtFinish = guestNow(); // #2366: and the guest's scope may have changed in it
|
|
4046
|
+
if (refusedAtFinish) {
|
|
4047
|
+
refuseGuest(refusedAtFinish);
|
|
4048
|
+
return;
|
|
4049
|
+
}
|
|
3895
4050
|
if (!deliverable()) {
|
|
3896
4051
|
gone();
|
|
3897
4052
|
return;
|