@songsid/agend 2.3.2-beta.9 → 2.3.3-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/README.md +6 -0
- package/README.zh-TW.md +6 -0
- package/dist/agent-cli-instructions.md +2 -2
- package/dist/agent-cli.js +48 -0
- package/dist/agent-cli.js.map +1 -1
- package/dist/agent-endpoint.js +6 -0
- package/dist/agent-endpoint.js.map +1 -1
- package/dist/agent-whats-new.d.ts +21 -0
- package/dist/agent-whats-new.js +53 -21
- package/dist/agent-whats-new.js.map +1 -1
- package/dist/auth-api.js +2 -0
- package/dist/auth-api.js.map +1 -1
- package/dist/backend/claude-code.d.ts +13 -1
- package/dist/backend/claude-code.js +43 -2
- package/dist/backend/claude-code.js.map +1 -1
- package/dist/backend/codex-session-lookup.d.ts +15 -0
- package/dist/backend/codex-session-lookup.js +58 -0
- package/dist/backend/codex-session-lookup.js.map +1 -1
- package/dist/backend/codex.js +15 -4
- package/dist/backend/codex.js.map +1 -1
- package/dist/backend/kiro-classic-archive.d.ts +57 -0
- package/dist/backend/kiro-classic-archive.js +622 -0
- package/dist/backend/kiro-classic-archive.js.map +1 -0
- package/dist/backend/kiro-classic-links.d.ts +24 -0
- package/dist/backend/kiro-classic-links.js +135 -0
- package/dist/backend/kiro-classic-links.js.map +1 -0
- package/dist/backend/kiro-identity.d.ts +100 -1
- package/dist/backend/kiro-identity.js +262 -31
- package/dist/backend/kiro-identity.js.map +1 -1
- package/dist/backend/kiro-migration-verify.d.ts +10 -0
- package/dist/backend/kiro-migration-verify.js +52 -0
- package/dist/backend/kiro-migration-verify.js.map +1 -0
- package/dist/backend/kiro-reconcile-store.d.ts +27 -0
- package/dist/backend/kiro-reconcile-store.js +351 -0
- package/dist/backend/kiro-reconcile-store.js.map +1 -0
- package/dist/backend/kiro-v2-store-worker.js +35 -1
- package/dist/backend/kiro-v2-store-worker.js.map +1 -1
- package/dist/backend/kiro-v2-store.d.ts +31 -1
- package/dist/backend/kiro-v2-store.js +22 -12
- package/dist/backend/kiro-v2-store.js.map +1 -1
- package/dist/backend/kiro.d.ts +124 -6
- package/dist/backend/kiro.js +553 -39
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/types.d.ts +109 -4
- package/dist/backend/types.js +9 -0
- package/dist/backend/types.js.map +1 -1
- package/dist/cache-service.d.ts +13 -1
- package/dist/cache-service.js +45 -2
- package/dist/cache-service.js.map +1 -1
- package/dist/channel/ipc-timeouts.js +3 -0
- package/dist/channel/ipc-timeouts.js.map +1 -1
- package/dist/channel/mcp-tools.js +40 -5
- package/dist/channel/mcp-tools.js.map +1 -1
- package/dist/channel/tool-router.js +2 -1
- package/dist/channel/tool-router.js.map +1 -1
- package/dist/classic-channel-manager.d.ts +6 -0
- package/dist/classic-channel-manager.js +25 -0
- package/dist/classic-channel-manager.js.map +1 -1
- package/dist/cli.js +52 -6
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.d.ts +7 -1
- package/dist/config-validator.js +20 -9
- package/dist/config-validator.js.map +1 -1
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/cross-instance-envelope.d.ts +10 -5
- package/dist/cross-instance-envelope.js +20 -17
- package/dist/cross-instance-envelope.js.map +1 -1
- package/dist/daemon.d.ts +125 -63
- package/dist/daemon.js +694 -330
- package/dist/daemon.js.map +1 -1
- package/dist/dashboard-permissions.d.ts +15 -4
- package/dist/dashboard-permissions.js +49 -23
- package/dist/dashboard-permissions.js.map +1 -1
- package/dist/delivery-outbox.d.ts +27 -22
- package/dist/delivery-outbox.js +109 -161
- package/dist/delivery-outbox.js.map +1 -1
- package/dist/delivery-outbox.test.js +10 -25
- package/dist/delivery-outbox.test.js.map +1 -1
- package/dist/doctor.js +2 -1
- package/dist/doctor.js.map +1 -1
- package/dist/fleet-control-audit.d.ts +1 -1
- package/dist/fleet-control-audit.js +3 -0
- package/dist/fleet-control-audit.js.map +1 -1
- package/dist/fleet-manager.d.ts +94 -15
- package/dist/fleet-manager.js +597 -133
- package/dist/fleet-manager.js.map +1 -1
- package/dist/forged-envelope-scan.d.ts +9 -0
- package/dist/forged-envelope-scan.js +30 -5
- package/dist/forged-envelope-scan.js.map +1 -1
- package/dist/general-instance.d.ts +24 -0
- package/dist/general-instance.js +53 -0
- package/dist/general-instance.js.map +1 -1
- package/dist/general-knowledge/skills/delegation-playbook/SKILL.md +1 -1
- package/dist/general-knowledge/skills/docker-install/SKILL.md +25 -0
- package/dist/general-knowledge/skills/fleet-config/SKILL.md +5 -3
- package/dist/general-knowledge/skills/fleet-restart/SKILL.md +1 -1
- package/dist/general-knowledge/skills/kiro-engine-migration/SKILL.md +34 -0
- package/dist/general-knowledge/skills/session-management/SKILL.md +2 -2
- package/dist/general-knowledge/skills/tui-effort/SKILL.md +1 -1
- package/dist/general-knowledge/skills/web-appearance/SKILL.canvas-worker-off.md +32 -0
- package/dist/general-knowledge/skills/web-appearance/SKILL.md +32 -0
- package/dist/general-knowledge/skills/web-dashboard/SKILL.canvas-worker-off.md +57 -17
- package/dist/general-knowledge/skills/web-dashboard/SKILL.md +57 -17
- package/dist/instance-config-impact.js +3 -2
- package/dist/instance-config-impact.js.map +1 -1
- package/dist/instance-lifecycle.d.ts +4 -0
- package/dist/instance-lifecycle.js +28 -5
- package/dist/instance-lifecycle.js.map +1 -1
- package/dist/instructions.js +6 -7
- package/dist/instructions.js.map +1 -1
- package/dist/kiro-migrate.d.ts +19 -2
- package/dist/kiro-migrate.js +133 -35
- package/dist/kiro-migrate.js.map +1 -1
- package/dist/kiro-reconcile.d.ts +21 -0
- package/dist/kiro-reconcile.js +88 -0
- package/dist/kiro-reconcile.js.map +1 -0
- package/dist/kiro-session-proof.d.ts +41 -0
- package/dist/kiro-session-proof.js +89 -0
- package/dist/kiro-session-proof.js.map +1 -0
- package/dist/kiro-tui-forensic.d.ts +19 -0
- package/dist/kiro-tui-forensic.js +124 -0
- package/dist/kiro-tui-forensic.js.map +1 -0
- package/dist/locale.js +202 -16
- package/dist/locale.js.map +1 -1
- package/dist/log-service.js +3 -3
- package/dist/log-service.js.map +1 -1
- package/dist/needs-you.d.ts +3 -1
- package/dist/needs-you.js +4 -2
- package/dist/needs-you.js.map +1 -1
- package/dist/office-levels.d.ts +64 -0
- package/dist/office-levels.js +66 -0
- package/dist/office-levels.js.map +1 -0
- package/dist/office-share-page.d.ts +3 -1
- package/dist/office-share-page.js +8 -3
- package/dist/office-share-page.js.map +1 -1
- package/dist/office-share-service.d.ts +6 -3
- package/dist/office-share-service.js +12 -8
- package/dist/office-share-service.js.map +1 -1
- package/dist/office-share.d.ts +45 -8
- package/dist/office-share.js +75 -15
- package/dist/office-share.js.map +1 -1
- package/dist/outbound-handlers.js +21 -2
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +47 -2
- package/dist/outbound-schemas.js +42 -3
- package/dist/outbound-schemas.js.map +1 -1
- package/dist/public-web-gateway.js +1 -1
- package/dist/public-web-gateway.js.map +1 -1
- package/dist/quickstart-api.d.ts +39 -1
- package/dist/quickstart-api.js +115 -17
- package/dist/quickstart-api.js.map +1 -1
- package/dist/remote-card-answer.d.ts +83 -0
- package/dist/remote-card-answer.js +208 -0
- package/dist/remote-card-answer.js.map +1 -0
- package/dist/remote-card-prompt.d.ts +38 -0
- package/dist/remote-card-prompt.js +86 -0
- package/dist/remote-card-prompt.js.map +1 -0
- package/dist/scheduler/task-board.d.ts +44 -17
- package/dist/scheduler/task-board.js +95 -36
- package/dist/scheduler/task-board.js.map +1 -1
- package/dist/sd-notify.d.ts +13 -14
- package/dist/sd-notify.js +62 -28
- package/dist/sd-notify.js.map +1 -1
- package/dist/service-installer.d.ts +13 -0
- package/dist/service-installer.js +15 -0
- package/dist/service-installer.js.map +1 -1
- package/dist/service-owner.d.ts +25 -0
- package/dist/service-owner.js +43 -2
- package/dist/service-owner.js.map +1 -1
- package/dist/settings-api.d.ts +2 -1
- package/dist/settings-api.js +19 -26
- package/dist/settings-api.js.map +1 -1
- package/dist/settings-change.js +10 -5
- package/dist/settings-change.js.map +1 -1
- package/dist/settings-effect.js +7 -6
- package/dist/settings-effect.js.map +1 -1
- package/dist/settings-instance-patch.js +1 -1
- package/dist/settings-instance-patch.js.map +1 -1
- package/dist/setup-form.d.ts +2 -2
- package/dist/setup-form.js +139 -93
- package/dist/setup-form.js.map +1 -1
- package/dist/setup-i18n.d.ts +25 -0
- package/dist/setup-i18n.js +226 -0
- package/dist/setup-i18n.js.map +1 -0
- package/dist/task-board-waker.d.ts +40 -1
- package/dist/task-board-waker.js +115 -9
- package/dist/task-board-waker.js.map +1 -1
- package/dist/tmux-control.d.ts +1 -1
- package/dist/tmux-control.js +3 -2
- package/dist/tmux-control.js.map +1 -1
- package/dist/tmux-manager.js +18 -16
- package/dist/tmux-manager.js.map +1 -1
- package/dist/tmux-read.d.ts +2 -0
- package/dist/tmux-read.js +7 -1
- package/dist/tmux-read.js.map +1 -1
- package/dist/tool-permissions.js +18 -0
- package/dist/tool-permissions.js.map +1 -1
- package/dist/topic-commands.d.ts +2 -0
- package/dist/topic-commands.js +2 -1
- package/dist/topic-commands.js.map +1 -1
- package/dist/transcript-sources.d.ts +1 -0
- package/dist/transcript-sources.js +20 -3
- package/dist/transcript-sources.js.map +1 -1
- package/dist/types.d.ts +11 -5
- package/dist/ui/backend-signin.js +4 -3
- package/dist/ui/chat-store.js +3 -2
- package/dist/ui/chat-strings.js +2 -2
- package/dist/ui/fleet-office-card.js +114 -0
- package/dist/ui/fleet-office-editor.js +81 -9
- package/dist/ui/fleet-office-layout.js +56 -11
- package/dist/ui/fleet-office-levels.js +105 -0
- package/dist/ui/fleet-office-model.js +9 -1
- package/dist/ui/fleet-office-pack.d.ts +15 -3
- package/dist/ui/fleet-office-pack.js +177 -23
- package/dist/ui/fleet-office-skin.js +39 -3
- package/dist/ui/fleet-office-store.js +74 -3
- package/dist/ui/fleet-office-view.js +8 -0
- package/dist/ui/fleet-office-zones.js +102 -19
- package/dist/ui/fleet-office.js +308 -44
- package/dist/ui/fleet-org.js +2 -0
- package/dist/ui/fleet-task-card.js +2 -2
- package/dist/ui/fleet-task-common.js +30 -7
- package/dist/ui/fleet-task-work.js +2 -2
- package/dist/ui/fleet-tasks.js +10 -11
- package/dist/ui/office-share-dialog.js +31 -7
- package/dist/ui/office-share-page.js +1 -0
- package/dist/ui/panel-chat.js +14 -2
- package/dist/ui/panel-log.js +6 -6
- package/dist/ui/panel-needs.js +1 -1
- package/dist/ui/panel-settings.js +16 -8
- package/dist/ui/settings-appearance.js +3 -35
- package/dist/ui/settings-discord.js +3 -2
- package/dist/ui/settings-guests.js +17 -7
- package/dist/ui/settings-model.js +17 -9
- package/dist/ui/settings-registry.js +1 -1
- package/dist/ui/settings-strings.js +18 -6
- package/dist/ui/settings-token.js +3 -2
- package/dist/ui/settings-wizard.js +25 -9
- package/dist/ui/shared/app-i18n.js +2 -2
- package/dist/ui/shared/app-stream.js +1 -1
- package/dist/ui/shared/app.css +70 -2
- package/dist/ui/shared/setup-host-text.js +107 -0
- package/dist/ui/structured-signin.js +89 -38
- package/dist/update-check.d.ts +13 -4
- package/dist/update-check.js +17 -4
- package/dist/update-check.js.map +1 -1
- package/dist/usage-rollup.d.ts +34 -3
- package/dist/usage-rollup.js +56 -3
- package/dist/usage-rollup.js.map +1 -1
- package/dist/web-api.d.ts +10 -1
- package/dist/web-api.js +207 -17
- package/dist/web-api.js.map +1 -1
- package/dist/web-auth.js +1 -1
- package/dist/web-auth.js.map +1 -1
- package/dist/web-experimental.d.ts +6 -2
- package/dist/web-experimental.js +8 -4
- package/dist/web-experimental.js.map +1 -1
- package/dist/web-office-acts.d.ts +5 -2
- package/dist/web-office-acts.js +9 -4
- package/dist/web-office-acts.js.map +1 -1
- package/dist/web-office-pack-read.d.ts +35 -0
- package/dist/web-office-pack-read.js +252 -0
- package/dist/web-office-pack-read.js.map +1 -0
- package/dist/web-office-pack-tools.d.ts +241 -0
- package/dist/web-office-pack-tools.js +785 -0
- package/dist/web-office-pack-tools.js.map +1 -0
- package/dist/web-office-skins.d.ts +68 -4
- package/dist/web-office-skins.js +324 -33
- package/dist/web-office-skins.js.map +1 -1
- package/dist/web-office-store.d.ts +47 -4
- package/dist/web-office-store.js +124 -23
- package/dist/web-office-store.js.map +1 -1
- package/dist/web-settings-patch.d.ts +3 -2
- package/dist/web-settings-patch.js +7 -5
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/workflow-templates/default.md +3 -3
- package/dist/working-recovery.d.ts +0 -2
- package/dist/working-recovery.js +2 -23
- package/dist/working-recovery.js.map +1 -1
- package/package.json +2 -2
- package/templates/windows-shortcuts.ps1 +18 -17
- package/dist/upgrade-notices.d.ts +0 -17
- package/dist/upgrade-notices.js +0 -93
- package/dist/upgrade-notices.js.map +0 -1
package/dist/web-api.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type { ThemeStore } from "./web-theme-store.js";
|
|
|
11
11
|
import type { OfficeStore, OfficeWriteLimiter } from "./web-office-store.js";
|
|
12
12
|
import { type OfficeSkinStore } from "./web-office-skins.js";
|
|
13
13
|
import type { OfficeActs } from "./web-office-acts.js";
|
|
14
|
+
import type { OfficePackTools } from "./web-office-pack-tools.js";
|
|
14
15
|
import type { WebAppearance } from "./web-appearance.js";
|
|
15
16
|
import type { SendOpts } from "./channel/types.js";
|
|
16
17
|
import type { IncomingMessage, ServerResponse } from "node:http";
|
|
@@ -67,6 +68,7 @@ export interface WebApiContext {
|
|
|
67
68
|
echo_to_channel?: boolean;
|
|
68
69
|
usage_panel?: boolean;
|
|
69
70
|
usage_report_on_public_link?: boolean;
|
|
71
|
+
office_levels?: boolean;
|
|
70
72
|
upload_max_file_mb?: number;
|
|
71
73
|
upload_max_total_mb?: number;
|
|
72
74
|
search?: {
|
|
@@ -196,6 +198,8 @@ export interface WebApiContext {
|
|
|
196
198
|
readonly themeStore?: ThemeStore;
|
|
197
199
|
/** Fleet → Org chart → Office: the offices people made (web-offices.json); absent: /ui/offices is not offered. */
|
|
198
200
|
readonly officeStore?: OfficeStore;
|
|
201
|
+
/** #2080: an instance dispatcher no longer in the fleet (its cards fall back to the person: Waiting for you). */
|
|
202
|
+
readonly dispatcherGone?: (name: string) => boolean;
|
|
199
203
|
/** How often a person may save the offices (absent: no limit, as in a test without it). */
|
|
200
204
|
readonly officeWrites?: OfficeWriteLimiter;
|
|
201
205
|
/** #2578: the agents' live acts in the Office view (office_set); absent: /ui/office-acts is not offered. */
|
|
@@ -214,6 +218,8 @@ export interface WebApiContext {
|
|
|
214
218
|
};
|
|
215
219
|
/** #2636: a share was made or ended from the page — said in General (who, which office, until when). */
|
|
216
220
|
officeShareNotice?(event: "created" | "revoked", who: string, share: import("./office-share.js").OfficeShare | null, count: number): void;
|
|
221
|
+
/** #2692: the agents' skin-pack changes (approval mode, pending changes, log); absent: /ui/office-pack-changes is not offered. */
|
|
222
|
+
readonly officePackTools?: Pick<OfficePackTools, "changes" | "setApproval" | "decide">;
|
|
217
223
|
/** #2513: the Office view's uploaded skin packs (web-office-skins/); absent: /ui/office-skins is not offered. */
|
|
218
224
|
readonly officeSkins?: OfficeSkinStore;
|
|
219
225
|
/** How often a person may upload or delete a skin pack (absent: no limit). */
|
|
@@ -255,6 +261,8 @@ export interface WebApiContext {
|
|
|
255
261
|
logSteps?(name: string, q: import("./log-service.js").LogQuery): Promise<unknown>;
|
|
256
262
|
/** #1468: the prompt-cache expiry analysis for a window ("24h" | "7d" | "30d"); absent: not offered. */
|
|
257
263
|
cacheReport?(window: string): Promise<unknown>;
|
|
264
|
+
/** #2661: every instance's Office level (office-levels.ts), reused for a minute; null: web.office_levels is off. Absent: not offered. */
|
|
265
|
+
officeLevels?(): Promise<Record<string, import("./office-levels.js").OfficeLevel> | null>;
|
|
258
266
|
/** #2122: the usage report (Fleet → Usage); absent: not offered. */
|
|
259
267
|
usageReport?(params: {
|
|
260
268
|
range: import("./usage-report.js").UsageRange;
|
|
@@ -324,7 +332,8 @@ export interface WebApiContext {
|
|
|
324
332
|
message: string;
|
|
325
333
|
};
|
|
326
334
|
/** Answer one of them, exactly as a click on its platform button would. */
|
|
327
|
-
|
|
335
|
+
/** `text`: #2675 — the typed answer, for the one action the page shows a text box for. */
|
|
336
|
+
clickWebPrompt?(instance: string, nonce: string, action: string, text?: string): Promise<{
|
|
328
337
|
status: number;
|
|
329
338
|
error?: string;
|
|
330
339
|
}>;
|
package/dist/web-api.js
CHANGED
|
@@ -450,7 +450,7 @@ function handleAppIcon(req, res, url, ctx, method) {
|
|
|
450
450
|
}
|
|
451
451
|
/**
|
|
452
452
|
* #2636: Office share links — GET /ui/office-share (the open ones, and whether this fleet offers them), POST { office,
|
|
453
|
-
* minutes?, text? } (a new one; answers its URL once its tunnel is up, or why not), DELETE /ui/office-share/<id|all>.
|
|
453
|
+
* minutes?, text?, theme? } (a new one; #2715 theme "auto" | "light" | "dark"; answers its URL once its tunnel is up, or why not), DELETE /ui/office-share/<id|all>.
|
|
454
454
|
* An admin session only — on the dashboard reached directly, or (the user, #2636 follow-up) a fleet admin signed in over
|
|
455
455
|
* the /web public link: the same check either way. Never the CLI token, a read-only session or a guest. The share's own
|
|
456
456
|
* tunnel and gateway stay apart from /web's. The person is asked again once the body is in.
|
|
@@ -515,12 +515,13 @@ function handleOfficeShare(req, res, url, ctx, path, method) {
|
|
|
515
515
|
return;
|
|
516
516
|
}
|
|
517
517
|
if (!body || typeof body !== "object" || Array.isArray(body) || typeof body.office !== "string"
|
|
518
|
-
|| (body.minutes !== undefined && typeof body.minutes !== "number") || (body.text !== undefined && typeof body.text !== "boolean")
|
|
519
|
-
|
|
518
|
+
|| (body.minutes !== undefined && typeof body.minutes !== "number") || (body.text !== undefined && typeof body.text !== "boolean")
|
|
519
|
+
|| (body.theme !== undefined && typeof body.theme !== "string")) {
|
|
520
|
+
json(res, 400, { error: "office (string), minutes? (number), text? (boolean), theme? (auto, light or dark)" });
|
|
520
521
|
return;
|
|
521
522
|
}
|
|
522
523
|
const made = await service.create({ office: body.office, owner: now.person, ...(typeof body.minutes === "number" ? { minutes: body.minutes } : {}),
|
|
523
|
-
...(typeof body.text === "boolean" ? { text: body.text } : {}) });
|
|
524
|
+
...(typeof body.text === "boolean" ? { text: body.text } : {}), ...(typeof body.theme === "string" ? { theme: body.theme } : {}) });
|
|
524
525
|
if (!made.ok) {
|
|
525
526
|
const status = made.error === "disabled" ? 404 : made.error === "tunnel" ? 503 : made.error === "too_many" ? 409 : 400;
|
|
526
527
|
json(res, status, { error: made.error, ...(made.reason ? { reason: made.reason } : {}) });
|
|
@@ -672,6 +673,92 @@ function handleOfficeSkins(req, res, url, ctx, path, method) {
|
|
|
672
673
|
});
|
|
673
674
|
return true;
|
|
674
675
|
}
|
|
676
|
+
/**
|
|
677
|
+
* #2692: the agents' skin-pack changes, for the Office page (Edit offices → Skin packs) — an admin session only, never
|
|
678
|
+
* the CLI token, a guest or the public link (as uploading a pack by hand). GET { approval, agents, pending, log };
|
|
679
|
+
* POST { approval: "apply" | "approve" } sets the fleet's approval mode; POST /<id> { decision: "approve" | "reject" }
|
|
680
|
+
* decides a pending change (judged again on a yes). Every change is `office_pack_changes` on the stream.
|
|
681
|
+
*/
|
|
682
|
+
function handleOfficePackChanges(req, res, url, ctx, path, method) {
|
|
683
|
+
const tools = ctx.officePackTools;
|
|
684
|
+
if (!tools) {
|
|
685
|
+
json(res, 404, { error: "Pack changes are not kept on this fleet" });
|
|
686
|
+
return true;
|
|
687
|
+
}
|
|
688
|
+
const one = /^\/ui\/office-pack-changes\/([0-9a-f]{32})$/.exec(path);
|
|
689
|
+
if (!(method === "GET" && path === "/ui/office-pack-changes") && !(method === "POST" && (path === "/ui/office-pack-changes" || one))) {
|
|
690
|
+
json(res, 404, { error: "not found" });
|
|
691
|
+
return true;
|
|
692
|
+
}
|
|
693
|
+
if (gatewayRequestContext(req)) {
|
|
694
|
+
json(res, 403, { error: "pack_changes_not_over_public_link" });
|
|
695
|
+
return true;
|
|
696
|
+
}
|
|
697
|
+
const who = () => {
|
|
698
|
+
const verdict = evaluateWebRequest(req, url, ctx.webToken ?? null, ctx.webSessions, { touch: false });
|
|
699
|
+
if (verdict.kind !== "allow")
|
|
700
|
+
return { status: 401 };
|
|
701
|
+
return verdict.via === "session" && verdict.session?.tier === "admin" ? { person: verdict.session.owner ? webPersonKey(verdict.session.owner) : "admin-session" } : { status: 403 };
|
|
702
|
+
};
|
|
703
|
+
const refuse = (status) => json(res, status, { error: status === 401 ? WEB_TOKEN_INVALID_MESSAGE : "pack_changes_need_an_admin_session" });
|
|
704
|
+
const first = who();
|
|
705
|
+
if ("status" in first) {
|
|
706
|
+
refuse(first.status);
|
|
707
|
+
return true;
|
|
708
|
+
}
|
|
709
|
+
if (method === "GET") {
|
|
710
|
+
res.setHeader("Cache-Control", "private, no-store");
|
|
711
|
+
json(res, 200, tools.changes());
|
|
712
|
+
return true;
|
|
713
|
+
}
|
|
714
|
+
(async () => {
|
|
715
|
+
let body;
|
|
716
|
+
try {
|
|
717
|
+
body = await parseBody(req);
|
|
718
|
+
}
|
|
719
|
+
catch {
|
|
720
|
+
json(res, 400, { error: "Invalid JSON" });
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
if (!permitWebContinuation(req, res, ctx))
|
|
724
|
+
return;
|
|
725
|
+
const now = who();
|
|
726
|
+
if ("status" in now) {
|
|
727
|
+
refuse(now.status);
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
if (!body || typeof body !== "object" || Array.isArray(body)) {
|
|
731
|
+
json(res, 400, { error: "an object" });
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
if (!one) {
|
|
735
|
+
if (Object.keys(body).some(k => k !== "approval")) {
|
|
736
|
+
json(res, 400, { error: "pack_changes_invalid", detail: "{ approval }" });
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
const r = tools.setApproval(body.approval, now.person);
|
|
740
|
+
if (!r.ok) {
|
|
741
|
+
json(res, 400, { error: "pack_changes_invalid", detail: r.error });
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
json(res, 200, tools.changes());
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
if (Object.keys(body).some(k => k !== "decision") || (body.decision !== "approve" && body.decision !== "reject")) {
|
|
748
|
+
json(res, 400, { error: "pack_changes_invalid", detail: '{ decision: "approve" | "reject" }' });
|
|
749
|
+
return;
|
|
750
|
+
}
|
|
751
|
+
const r = tools.decide(one[1], body.decision, now.person);
|
|
752
|
+
json(res, r.ok ? 200 : r.status, r.ok ? { text: r.text, ...(r.applied ? { applied: r.applied } : {}) } : { error: r.error, detail: r.text });
|
|
753
|
+
})().catch(err => {
|
|
754
|
+
ctx.logger.error({ err: err.message }, "A pack change failed");
|
|
755
|
+
try {
|
|
756
|
+
json(res, 500, { error: "A pack change failed" });
|
|
757
|
+
}
|
|
758
|
+
catch { /* already answered */ }
|
|
759
|
+
});
|
|
760
|
+
return true;
|
|
761
|
+
}
|
|
675
762
|
/**
|
|
676
763
|
* #1629 r4 §3: the owner-CSS routes (see their call site). `who()`, asked first and again after any body: the person a
|
|
677
764
|
* signed-in human session is, or the status to answer.
|
|
@@ -2398,6 +2485,75 @@ export function handleWebRequest(req, res, url, ctx) {
|
|
|
2398
2485
|
});
|
|
2399
2486
|
return true;
|
|
2400
2487
|
}
|
|
2488
|
+
// #2716: PUT /ui/office-style { skin }: the whole-fleet office's style for every viewer, device and share link (a skin
|
|
2489
|
+
// id, or null for the default; read back as GET /ui/offices's autoSkin). Who may save the offices may set it, as they
|
|
2490
|
+
// would: an admin session, never the CLI token or the public link; asked again once the body is in; counted with the
|
|
2491
|
+
// offices' saves. Every page hears `offices` and reads again.
|
|
2492
|
+
if (method === "PUT" && path === "/ui/office-style") {
|
|
2493
|
+
const store = ctx.officeStore;
|
|
2494
|
+
if (!store) {
|
|
2495
|
+
json(res, 404, { error: "Offices are not kept on this fleet" });
|
|
2496
|
+
return true;
|
|
2497
|
+
}
|
|
2498
|
+
if (gatewayRequestContext(req)) {
|
|
2499
|
+
json(res, 403, { error: "offices_not_over_public_link" });
|
|
2500
|
+
return true;
|
|
2501
|
+
}
|
|
2502
|
+
const who = () => {
|
|
2503
|
+
const verdict = evaluateWebRequest(req, url, ctx.webToken ?? null, ctx.webSessions, { touch: false });
|
|
2504
|
+
if (verdict.kind !== "allow")
|
|
2505
|
+
return { status: 401 };
|
|
2506
|
+
return verdict.via === "session" && verdict.session?.tier === "admin" ? { person: verdict.session.owner ? webPersonKey(verdict.session.owner) : "admin-session" } : { status: 403 };
|
|
2507
|
+
};
|
|
2508
|
+
const refuse = (status) => json(res, status, { error: status === 401 ? WEB_TOKEN_INVALID_MESSAGE : "offices_need_an_admin_session" });
|
|
2509
|
+
const first = who();
|
|
2510
|
+
if ("status" in first) {
|
|
2511
|
+
refuse(first.status);
|
|
2512
|
+
return true;
|
|
2513
|
+
}
|
|
2514
|
+
(async () => {
|
|
2515
|
+
let body;
|
|
2516
|
+
try {
|
|
2517
|
+
body = await parseBody(req);
|
|
2518
|
+
}
|
|
2519
|
+
catch {
|
|
2520
|
+
json(res, 400, { error: "Invalid JSON" });
|
|
2521
|
+
return;
|
|
2522
|
+
}
|
|
2523
|
+
if (!permitWebContinuation(req, res, ctx))
|
|
2524
|
+
return;
|
|
2525
|
+
const now = who();
|
|
2526
|
+
if ("status" in now) {
|
|
2527
|
+
refuse(now.status);
|
|
2528
|
+
return;
|
|
2529
|
+
}
|
|
2530
|
+
if (!body || typeof body !== "object" || Array.isArray(body) || !("skin" in body) || Object.keys(body).some(k => k !== "skin")) {
|
|
2531
|
+
json(res, 400, { error: "office_style_invalid", detail: "{ skin: a skin id, or null }" });
|
|
2532
|
+
return;
|
|
2533
|
+
}
|
|
2534
|
+
const limiter = ctx.officeWrites, allowed = limiter ? limiter.check(now.person) : { ok: true };
|
|
2535
|
+
if (!allowed.ok) {
|
|
2536
|
+
res.setHeader("Retry-After", String(allowed.retry_after_s));
|
|
2537
|
+
json(res, 429, { error: "offices_rate_limited", retry_after_s: allowed.retry_after_s });
|
|
2538
|
+
return;
|
|
2539
|
+
}
|
|
2540
|
+
const r = store.setAutoSkin(body.skin);
|
|
2541
|
+
if (!r.ok) {
|
|
2542
|
+
json(res, r.status, { error: r.error, ...(r.detail ? { detail: r.detail } : {}) });
|
|
2543
|
+
return;
|
|
2544
|
+
}
|
|
2545
|
+
limiter?.record(now.person);
|
|
2546
|
+
ctx.emitSseEvent("offices", { rev: r.rev });
|
|
2547
|
+
json(res, 200, { rev: r.rev, autoSkin: r.autoSkin });
|
|
2548
|
+
})().catch(err => {
|
|
2549
|
+
ctx.logger.error({ err: err.message }, "Saving the office style failed");
|
|
2550
|
+
try {
|
|
2551
|
+
json(res, 500, { error: "Saving the office style failed" });
|
|
2552
|
+
}
|
|
2553
|
+
catch { /* already answered */ }
|
|
2554
|
+
});
|
|
2555
|
+
return true;
|
|
2556
|
+
}
|
|
2401
2557
|
// #2578: GET /ui/office-acts { acts }: the agents' live acts (office_set), read as the offices are (the public link
|
|
2402
2558
|
// too, which has no stream); every change also comes as `office_acts` on the stream. Nothing here writes one.
|
|
2403
2559
|
if (method === "GET" && path === "/ui/office-acts") {
|
|
@@ -2409,8 +2565,41 @@ export function handleWebRequest(req, res, url, ctx) {
|
|
|
2409
2565
|
json(res, 200, { acts: acts.live(), now: Date.now() }); // now: the host's clock, for the page's offset
|
|
2410
2566
|
return true;
|
|
2411
2567
|
}
|
|
2568
|
+
// #2661: GET /ui/office-levels { enabled, unit, detail, levels }: each instance's Office level, read as the offices
|
|
2569
|
+
// are (a guest's routes do not include it). The level for whoever may open the Office; the numbers behind it (work
|
|
2570
|
+
// tokens, today, first seen) only where the usage report would be served (`detail`): not with web.usage_panel off,
|
|
2571
|
+
// not over the public link unless web.usage_report_on_public_link. Off (web.office_levels false): no levels at all.
|
|
2572
|
+
if (method === "GET" && path === "/ui/office-levels") {
|
|
2573
|
+
if (!ctx.officeLevels) {
|
|
2574
|
+
json(res, 404, { error: "Office levels are not kept on this fleet" });
|
|
2575
|
+
return true;
|
|
2576
|
+
}
|
|
2577
|
+
const admitted = admissionOf(req, url, ctx);
|
|
2578
|
+
if (!admitted) {
|
|
2579
|
+
json(res, 401, { error: WEB_TOKEN_INVALID_MESSAGE });
|
|
2580
|
+
return true;
|
|
2581
|
+
}
|
|
2582
|
+
const detail = () => ctx.fleetConfig?.web?.usage_panel !== false
|
|
2583
|
+
&& (!gatewayRequestContext(req) || ctx.fleetConfig?.web?.usage_report_on_public_link === true);
|
|
2584
|
+
void import("./office-levels.js").then(async ({ LEVEL, levelsFor }) => {
|
|
2585
|
+
const levels = await ctx.officeLevels();
|
|
2586
|
+
// Asked again before it is sent (it was awaited): a session signed out or a switch turned off meanwhile.
|
|
2587
|
+
if (admissionOf(req, url, ctx) !== admitted)
|
|
2588
|
+
return json(res, 401, { error: WEB_TOKEN_INVALID_MESSAGE });
|
|
2589
|
+
if (!levels || ctx.fleetConfig?.web?.office_levels === false)
|
|
2590
|
+
return json(res, 200, { enabled: false, unit: LEVEL.UNIT, detail: false, levels: {} });
|
|
2591
|
+
const d = detail();
|
|
2592
|
+
json(res, 200, { enabled: true, unit: LEVEL.UNIT, detail: d, levels: levelsFor(levels, d) });
|
|
2593
|
+
}).catch((err) => { ctx.logger.error({ err }, "office levels failed"); try {
|
|
2594
|
+
json(res, 500, { error: "Office levels failed" });
|
|
2595
|
+
}
|
|
2596
|
+
catch { /* already answered */ } });
|
|
2597
|
+
return true;
|
|
2598
|
+
}
|
|
2412
2599
|
if (path === "/ui/office-skins" || path.startsWith("/ui/office-skins/"))
|
|
2413
2600
|
return handleOfficeSkins(req, res, url, ctx, path, method);
|
|
2601
|
+
if (path === "/ui/office-pack-changes" || path.startsWith("/ui/office-pack-changes/"))
|
|
2602
|
+
return handleOfficePackChanges(req, res, url, ctx, path, method);
|
|
2414
2603
|
if (path === "/ui/office-share" || path.startsWith("/ui/office-share/"))
|
|
2415
2604
|
return handleOfficeShare(req, res, url, ctx, path, method);
|
|
2416
2605
|
// ── #1630: avatars and Discord server emoji, same-origin (the page never loads a platform's CDN; img-src stays 'self') ──
|
|
@@ -2500,12 +2689,12 @@ export function handleWebRequest(req, res, url, ctx) {
|
|
|
2500
2689
|
json(res, 400, { error: "instance, nonce and action required" });
|
|
2501
2690
|
return;
|
|
2502
2691
|
}
|
|
2503
|
-
const { instance, nonce, action } = body;
|
|
2504
|
-
if (typeof instance !== "string" || typeof nonce !== "string" || typeof action !== "string") {
|
|
2692
|
+
const { instance, nonce, action, text } = body;
|
|
2693
|
+
if (typeof instance !== "string" || typeof nonce !== "string" || typeof action !== "string" || (text !== undefined && typeof text !== "string")) {
|
|
2505
2694
|
json(res, 400, { error: "instance, nonce and action required" });
|
|
2506
2695
|
return;
|
|
2507
2696
|
}
|
|
2508
|
-
const r = await click(instance, nonce, action);
|
|
2697
|
+
const r = text === undefined ? await click(instance, nonce, action) : await click(instance, nonce, action, text);
|
|
2509
2698
|
// 409: answered elsewhere first, expired, or never open — the page drops the buttons on `gone`.
|
|
2510
2699
|
json(res, r.status, r.status === 200 ? { answered: true } : { error: r.error ?? "Refused", ...(r.status === 409 ? { gone: true } : {}) });
|
|
2511
2700
|
})().catch(err => {
|
|
@@ -3231,7 +3420,8 @@ export function handleWebRequest(req, res, url, ctx) {
|
|
|
3231
3420
|
// #2296: every open Tasks tab reads the board again after each change; on the dashboard they all get the same
|
|
3232
3421
|
// board for a revision, so it is built once per (revision, done=all, minute) — the minute for the 14-day
|
|
3233
3422
|
// window. The public link's reads are filtered per request (hidden instances) and are built each time.
|
|
3234
|
-
|
|
3423
|
+
// #2757 review: and by the dispatchers gone (a deleted one makes its cards the person's, the revision unchanged).
|
|
3424
|
+
const key = gateway ? null : `${rev}:${allDone ? 1 : 0}:${Math.floor(Date.now() / 60_000)}:${board.goneDispatchers(ctx.dispatcherGone)}`;
|
|
3235
3425
|
const kept = key ? boardBodies.get(board) : undefined;
|
|
3236
3426
|
if (kept && kept.key === key) {
|
|
3237
3427
|
jsonText(res, 200, kept.body);
|
|
@@ -3243,7 +3433,7 @@ export function handleWebRequest(req, res, url, ctx) {
|
|
|
3243
3433
|
// #2355 review: the names a project's lead may be — the fleet's instances (a Classic-only room is not one); over the
|
|
3244
3434
|
// public link, not a hidden one.
|
|
3245
3435
|
const leads = Object.keys(ctx.fleetConfig?.instances ?? {}).filter((n) => !hiddenName(n)).sort();
|
|
3246
|
-
const body = JSON.stringify({ rev, cards: board.views(cards, scope), projects, leads, stats: board.projectStats() });
|
|
3436
|
+
const body = JSON.stringify({ rev, cards: board.views(cards, scope, ctx.dispatcherGone), projects, leads, stats: board.projectStats(ctx.dispatcherGone) });
|
|
3247
3437
|
if (key)
|
|
3248
3438
|
boardBodies.set(board, { key, body });
|
|
3249
3439
|
jsonText(res, 200, body);
|
|
@@ -3258,7 +3448,7 @@ export function handleWebRequest(req, res, url, ctx) {
|
|
|
3258
3448
|
// by every open drawer (the public link's is filtered per request, as the board's).
|
|
3259
3449
|
const only = url.searchParams.get("instance");
|
|
3260
3450
|
const names = (only ? [only] : [...new Set([...Object.keys(ctx.fleetConfig?.instances ?? {}), ...board.liveAssignees()])]).filter((n) => !hiddenName(n));
|
|
3261
|
-
const key = gateway ? null : `${board.revision()}:${only === null ? "*" : `=${only}`}:${names.join("\u0000")}`;
|
|
3451
|
+
const key = gateway ? null : `${board.revision()}:${only === null ? "*" : `=${only}`}:${names.join("\u0000")}:${board.goneDispatchers(ctx.dispatcherGone)}`;
|
|
3262
3452
|
const kept = key ? workBodies.get(board) : undefined;
|
|
3263
3453
|
if (kept && kept.key === key) {
|
|
3264
3454
|
jsonText(res, 200, kept.body);
|
|
@@ -3271,7 +3461,7 @@ export function handleWebRequest(req, res, url, ctx) {
|
|
|
3271
3461
|
for (const c of [...w.doing, ...w.blocked, ...w.review, ...w.ready, ...w.waiting.map((x) => x.card), ...w.mains.map((x) => x.card)])
|
|
3272
3462
|
if (!hidden(c))
|
|
3273
3463
|
all.set(c.uuid, c);
|
|
3274
|
-
const viewOf = new Map(board.views([...all.values()], scope).map((v) => [v.uuid, v]));
|
|
3464
|
+
const viewOf = new Map(board.views([...all.values()], scope, ctx.dispatcherGone).map((v) => [v.uuid, v]));
|
|
3275
3465
|
const drawn = (cards) => cards.flatMap((c) => (viewOf.has(c.uuid) ? [viewOf.get(c.uuid)] : []));
|
|
3276
3466
|
const codes = (cards) => cards.filter((x) => all.has(x.card.uuid)).map((x) => x.card.code);
|
|
3277
3467
|
const body = JSON.stringify({ instances: works.map(({ name, w }) => ({
|
|
@@ -3288,7 +3478,7 @@ export function handleWebRequest(req, res, url, ctx) {
|
|
|
3288
3478
|
if (method === "GET" && path === "/ui/tasks/search") {
|
|
3289
3479
|
const names = new Map(board.listProjects(true).map((p) => [p.id, p.name]));
|
|
3290
3480
|
const hits = board.search(url.searchParams.get("q") ?? "", Number(url.searchParams.get("limit")) || 20, scope).filter((h) => !hidden(h.card));
|
|
3291
|
-
const views = board.views(hits.map((h) => h.card), scope);
|
|
3481
|
+
const views = board.views(hits.map((h) => h.card), scope, ctx.dispatcherGone);
|
|
3292
3482
|
const results = hits.map(({ card: c, match }, i) => ({
|
|
3293
3483
|
card: views[i],
|
|
3294
3484
|
match,
|
|
@@ -3304,13 +3494,13 @@ export function handleWebRequest(req, res, url, ctx) {
|
|
|
3304
3494
|
json(res, 404, { error: "no such card", code: "not_found" });
|
|
3305
3495
|
return true;
|
|
3306
3496
|
}
|
|
3307
|
-
json(res, 200, shown(board.detail(ref, scope)));
|
|
3497
|
+
json(res, 200, shown(board.detail(ref, scope, ctx.dispatcherGone)));
|
|
3308
3498
|
return true;
|
|
3309
3499
|
}
|
|
3310
3500
|
if (method === "POST" && path === "/ui/tasks/cards") {
|
|
3311
3501
|
boardWrite(req, res, url, ctx, board, CardCreateSchema, (spec, person) => {
|
|
3312
3502
|
const { cards, said } = board.createCards(person, { cards: [spec] });
|
|
3313
|
-
return { card: board.view(cards[0]), notes: said };
|
|
3503
|
+
return { card: board.view(cards[0], {}, ctx.dispatcherGone), notes: said };
|
|
3314
3504
|
});
|
|
3315
3505
|
return true;
|
|
3316
3506
|
}
|
|
@@ -3318,16 +3508,16 @@ export function handleWebRequest(req, res, url, ctx) {
|
|
|
3318
3508
|
if (card[2] === "move") {
|
|
3319
3509
|
boardWrite(req, res, url, ctx, board, CardMoveSchema, ({ to, ...move }, person) => {
|
|
3320
3510
|
const { card: moved, said } = board.moveCard(person, ref, to, move);
|
|
3321
|
-
return { card: board.view(moved), notes: said };
|
|
3511
|
+
return { card: board.view(moved, {}, ctx.dispatcherGone), notes: said };
|
|
3322
3512
|
});
|
|
3323
3513
|
}
|
|
3324
3514
|
else if (card[2] === "reorder") {
|
|
3325
|
-
boardWrite(req, res, url, ctx, board, CardReorderSchema, ({ after, before }, person) => ({ card: board.view(board.reorder(person, ref, after ?? null, before ?? null)) }));
|
|
3515
|
+
boardWrite(req, res, url, ctx, board, CardReorderSchema, ({ after, before }, person) => ({ card: board.view(board.reorder(person, ref, after ?? null, before ?? null), {}, ctx.dispatcherGone) }));
|
|
3326
3516
|
}
|
|
3327
3517
|
else {
|
|
3328
3518
|
boardWrite(req, res, url, ctx, board, CardPatchSchema, (patch, person) => {
|
|
3329
3519
|
const { card: updated, said } = board.updateCard(person, ref, patch);
|
|
3330
|
-
return { card: board.view(updated), notes: said };
|
|
3520
|
+
return { card: board.view(updated, {}, ctx.dispatcherGone), notes: said };
|
|
3331
3521
|
});
|
|
3332
3522
|
}
|
|
3333
3523
|
return true;
|