@songsid/agend 2.3.2-beta.9 → 2.3.2
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.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.js +3 -0
- 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.js +23 -1
- 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 +344 -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 -1
- package/dist/backend/kiro.js +553 -29
- package/dist/backend/kiro.js.map +1 -1
- package/dist/backend/types.d.ts +102 -0
- 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 +50 -4
- package/dist/cli.js.map +1 -1
- package/dist/config-validator.d.ts +7 -1
- package/dist/config-validator.js +17 -8
- package/dist/config-validator.js.map +1 -1
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/daemon.d.ts +66 -0
- package/dist/daemon.js +421 -19
- 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/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 +90 -3
- package/dist/fleet-manager.js +569 -41
- 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/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 +1 -1
- 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 +7 -0
- package/dist/instance-lifecycle.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 +198 -0
- package/dist/locale.js.map +1 -1
- package/dist/log-service.js +2 -2
- 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 +20 -1
- package/dist/outbound-handlers.js.map +1 -1
- package/dist/outbound-schemas.d.ts +47 -2
- package/dist/outbound-schemas.js +41 -2
- 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 +28 -6
- package/dist/scheduler/task-board.js +66 -11
- package/dist/scheduler/task-board.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 -0
- package/dist/settings-api.js +18 -12
- 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 -4
- 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.js +7 -2
- package/dist/task-board-waker.js.map +1 -1
- package/dist/tool-permissions.js +18 -0
- package/dist/tool-permissions.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 +10 -0
- 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 +4 -4
- package/dist/ui/panel-needs.js +1 -1
- package/dist/ui/panel-settings.js +16 -8
- 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 -0
- package/dist/ui/settings-strings.js +16 -4
- 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/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-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 -1
- package/dist/web-settings-patch.js +4 -1
- package/dist/web-settings-patch.js.map +1 -1
- package/dist/workflow-templates/default.md +1 -1
- package/package.json +2 -2
- package/templates/windows-shortcuts.ps1 +18 -17
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { ESTIMATED_BACKENDS } from "./usage-report.js";
|
|
2
|
+
import { hourSources, localDate } from "./usage-rollup.js";
|
|
3
|
+
export const LEVEL = { UNIT: 100_000 };
|
|
4
|
+
/** How long a worked-out set of levels is reused before the next request works it out again. */
|
|
5
|
+
export const OFFICE_LEVELS_TTL_MS = 60_000;
|
|
6
|
+
/** The work in a totals entry ([requests, uncached, read, write5m, write1h, write, output]): everything but reads. */
|
|
7
|
+
export const workOf = (t) => t[1] + t[3] + t[4] + t[5] + t[6];
|
|
8
|
+
/** The level `work` tokens reach. */
|
|
9
|
+
export const levelOf = (work) => Math.floor(Math.sqrt(Math.max(0, work) / LEVEL.UNIT));
|
|
10
|
+
const dict = () => Object.create(null);
|
|
11
|
+
/** Every instance's level, by name. */
|
|
12
|
+
export function officeLevels(input) {
|
|
13
|
+
const today = localDate(input.now, input.zone);
|
|
14
|
+
const out = dict();
|
|
15
|
+
for (const { name, ledger, days, life } of input.instances) {
|
|
16
|
+
let work = 0, estimated = false, todayWork = 0, todayRequests = 0, first = null;
|
|
17
|
+
const count = (backend, date, t) => {
|
|
18
|
+
const w = workOf(t);
|
|
19
|
+
if (w <= 0 && t[0] <= 0)
|
|
20
|
+
return;
|
|
21
|
+
work += w;
|
|
22
|
+
if (w > 0 && ESTIMATED_BACKENDS.has(backend))
|
|
23
|
+
estimated = true;
|
|
24
|
+
if (first === null || date < first)
|
|
25
|
+
first = date;
|
|
26
|
+
if (date === today) {
|
|
27
|
+
todayWork += w;
|
|
28
|
+
todayRequests += t[0];
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
if (life.first !== null)
|
|
32
|
+
for (const [be, t] of Object.entries(life.by))
|
|
33
|
+
count(be, life.first, t);
|
|
34
|
+
for (const row of days.rows) {
|
|
35
|
+
if (life.through !== null && row.date <= life.through)
|
|
36
|
+
continue; // carried already (a crash before the prune)
|
|
37
|
+
for (const t of Object.values(row.m))
|
|
38
|
+
count(row.be, row.date, t);
|
|
39
|
+
}
|
|
40
|
+
for (const [hk, hour] of Object.entries(ledger.hours)) {
|
|
41
|
+
const h = Number(hk);
|
|
42
|
+
if (h <= days.checkpoint)
|
|
43
|
+
continue; // already in the rollup: counted once (usage-rollup.ts)
|
|
44
|
+
const date = localDate(h, input.zone);
|
|
45
|
+
for (const src of hourSources(hour))
|
|
46
|
+
for (const t of Object.values(src.m))
|
|
47
|
+
count(src.backend, date, t);
|
|
48
|
+
}
|
|
49
|
+
out[name] = { level: levelOf(work), workTokens: work, estimated, today: { workTokens: todayWork, requests: todayRequests }, firstSeen: first };
|
|
50
|
+
}
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* What a viewer may read of the levels: the level alone (share links, a viewer the usage report is not served to), or
|
|
55
|
+
* the whole entry. `names`: only these (a share's people); omitted, every one.
|
|
56
|
+
*/
|
|
57
|
+
export function levelsFor(levels, detail, names) {
|
|
58
|
+
const out = dict();
|
|
59
|
+
for (const [name, l] of Object.entries(levels)) {
|
|
60
|
+
if (names && !names.has(name))
|
|
61
|
+
continue;
|
|
62
|
+
out[name] = detail ? { ...l, today: { ...l.today } } : { level: l.level };
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=office-levels.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"office-levels.js","sourceRoot":"","sources":["../src/office-levels.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,5 +12,7 @@ export declare function officeShareStatic(path: string): {
|
|
|
12
12
|
} | null;
|
|
13
13
|
/** The page's policy: its own scripts, styles, pictures and reads; nothing inline, nothing framed, no form. */
|
|
14
14
|
export declare const OFFICE_SHARE_PAGE_CSP: string;
|
|
15
|
-
/** The page itself: the same for every share (the module reads the token from the address). */
|
|
15
|
+
/** The page itself: the same for every share of a look (the module reads the token from the address). */
|
|
16
16
|
export declare const OFFICE_SHARE_PAGE = "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"utf-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1, viewport-fit=cover\">\n<meta name=\"referrer\" content=\"no-referrer\">\n<meta name=\"robots\" content=\"noindex, nofollow\">\n<title>AgEnD Office</title>\n<link rel=\"stylesheet\" href=\"/assets/tokens.css\">\n<link rel=\"stylesheet\" href=\"/assets/app.css\">\n<script type=\"module\" src=\"/ui/js/office-share-page.js\"></script>\n</head>\n<body>\n<div id=\"app\" class=\"app-root\" data-share=\"1\"></div>\n<noscript><p class=\"noscript\">This live Office view needs JavaScript.</p></noscript>\n</body>\n</html>\n";
|
|
17
|
+
/** #2715: the page in a share's look — "light" or "dark" on <html data-theme> (tokens.css's own switch); "auto": as it is. */
|
|
18
|
+
export declare function officeSharePage(theme: string): string;
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* #2636: an Office share link's page and the static files it loads, as the share gateway serves them (office-share.ts).
|
|
3
3
|
*
|
|
4
|
-
* The page (`/o/<token>`) is a fixed document — no data in it
|
|
4
|
+
* The page (`/o/<token>`) is a fixed document — no data in it but its look (#2715: the share's light or dark, set on
|
|
5
|
+
* <html> so it paints in it at once; "auto" leaves the viewer's device to choose), no inline script — that loads the share page's module
|
|
5
6
|
* (src/ui/office-share-page.js), which reads the share's own addresses. Its modules and stylesheets are served at the
|
|
6
7
|
* dashboard's own paths (`/assets/<name>`, `/ui/js/<name>`) so the Office's imports work unchanged, without a token:
|
|
7
8
|
* they are the app's static code (published with AgEnD), never data — and only the files named here, the page
|
|
@@ -29,7 +30,7 @@ export const SHARE_ASSET_FILES = { "inter.woff2": join("fonts", "inter.woff2"),
|
|
|
29
30
|
export const SHARE_MODULES = [
|
|
30
31
|
"office-share-page.js", "fleet-office.js", "fleet-office-model.js", "fleet-office-skin.js", "fleet-office-zones.js", "fleet-office-store.js",
|
|
31
32
|
"fleet-office-editor.js", "fleet-office-layout.js", "fleet-office-pack.js", "fleet-office-overview.js", "fleet-office-view.js",
|
|
32
|
-
"fleet-org.js", "fleet-canvas-layout.js", "fleet-canvas-view.js", "fleet-traffic.js", "office-share-dialog.js",
|
|
33
|
+
"fleet-org.js", "fleet-canvas-layout.js", "fleet-canvas-view.js", "fleet-traffic.js", "office-share-dialog.js", "fleet-office-levels.js", "fleet-office-card.js",
|
|
33
34
|
];
|
|
34
35
|
const cache = new Map();
|
|
35
36
|
const read = (file) => {
|
|
@@ -63,7 +64,7 @@ export function officeShareStatic(path) {
|
|
|
63
64
|
/** The page's policy: its own scripts, styles, pictures and reads; nothing inline, nothing framed, no form. */
|
|
64
65
|
export const OFFICE_SHARE_PAGE_CSP = "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self' data: blob:; font-src 'self'; "
|
|
65
66
|
+ "connect-src 'self'; base-uri 'none'; form-action 'none'; frame-ancestors 'none'";
|
|
66
|
-
/** The page itself: the same for every share (the module reads the token from the address). */
|
|
67
|
+
/** The page itself: the same for every share of a look (the module reads the token from the address). */
|
|
67
68
|
export const OFFICE_SHARE_PAGE = `<!DOCTYPE html>
|
|
68
69
|
<html lang="en">
|
|
69
70
|
<head>
|
|
@@ -82,4 +83,8 @@ export const OFFICE_SHARE_PAGE = `<!DOCTYPE html>
|
|
|
82
83
|
</body>
|
|
83
84
|
</html>
|
|
84
85
|
`;
|
|
86
|
+
/** #2715: the page in a share's look — "light" or "dark" on <html data-theme> (tokens.css's own switch); "auto": as it is. */
|
|
87
|
+
export function officeSharePage(theme) {
|
|
88
|
+
return theme === "light" || theme === "dark" ? OFFICE_SHARE_PAGE.replace('<html lang="en">', `<html lang="en" data-theme="${theme}">`) : OFFICE_SHARE_PAGE;
|
|
89
|
+
}
|
|
85
90
|
//# sourceMappingURL=office-share-page.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"office-share-page.js","sourceRoot":"","sources":["../src/office-share-page.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"office-share-page.js","sourceRoot":"","sources":["../src/office-share-page.ts"],"names":[],"mappings":"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"}
|
|
@@ -17,7 +17,7 @@ import type { TunnelProvider } from "./tunnel/types.js";
|
|
|
17
17
|
export declare const OFFICE_SHARE_TUNNEL_DIR = "office-share-tunnel";
|
|
18
18
|
/** How often open shares are checked against who may still have them. */
|
|
19
19
|
export declare const SWEEP_MS = 30000;
|
|
20
|
-
export type OfficeShareRefusal = "disabled" | "office" | "duration" | "too_many" | "tunnel";
|
|
20
|
+
export type OfficeShareRefusal = "disabled" | "office" | "duration" | "too_many" | "theme" | "tunnel";
|
|
21
21
|
export type OfficeShareMade = {
|
|
22
22
|
ok: true;
|
|
23
23
|
share: OfficeShare;
|
|
@@ -61,17 +61,20 @@ export declare class OfficeShareService {
|
|
|
61
61
|
private abort;
|
|
62
62
|
private unsubscribe;
|
|
63
63
|
private readonly limiter;
|
|
64
|
+
/** #2662: the shares' pack pictures, each share's own minute — kept here so an ended share's key goes with it. */
|
|
65
|
+
private readonly pictureLimiter;
|
|
64
66
|
constructor(deps: OfficeShareServiceDeps);
|
|
65
67
|
enabled(): boolean;
|
|
66
68
|
list(): OfficeShare[];
|
|
67
69
|
/**
|
|
68
|
-
* A new share and its URL, once the tunnel is up. `minutes` 15–max_hours×60 (60 if not given); `office` ALL_OFFICES
|
|
69
|
-
* or a stored office's id. A tunnel that cannot start ends the share at once and says why.
|
|
70
|
+
* A new share and its URL, once the tunnel is up. `minutes` 15–max_hours×60 (60 if not given); `office` ALL_OFFICES,
|
|
71
|
+
* WHOLE_FLEET (#2716) or a stored office's id; `theme` (#2715) "auto", "light" or "dark". A tunnel that cannot start ends the share at once and says why.
|
|
70
72
|
*/
|
|
71
73
|
create(opts: {
|
|
72
74
|
office: string;
|
|
73
75
|
minutes?: number;
|
|
74
76
|
text?: boolean;
|
|
77
|
+
theme?: string;
|
|
75
78
|
owner: string;
|
|
76
79
|
}): Promise<OfficeShareMade>;
|
|
77
80
|
revoke(id: string): number;
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
* - Its own manager and lease (OFFICE_SHARE_TUNNEL_DIR): never the fleet's one tunnel lane (/web, /login) nor the
|
|
10
10
|
* public link's preview tunnel. A tunnel that goes away unexpectedly ends every share (their address was its host).
|
|
11
11
|
*/
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
12
|
+
import { fleetWide, OfficeShareLimiter, OfficeShares, createOfficeShareGateway, shareMaxMinutes } from "./office-share.js";
|
|
13
|
+
import { OFFICE_SHARE_PAGE_CSP, officeSharePage, officeShareStatic } from "./office-share-page.js";
|
|
14
14
|
import { failureOf } from "./public-link-progress.js";
|
|
15
15
|
import { newTunnelSid } from "./tunnel/manager.js";
|
|
16
16
|
/** Where the share tunnel keeps its own lease (never the fleet's `tunnel.lease`). */
|
|
@@ -30,6 +30,8 @@ export class OfficeShareService {
|
|
|
30
30
|
abort = null;
|
|
31
31
|
unsubscribe;
|
|
32
32
|
limiter = new OfficeShareLimiter();
|
|
33
|
+
/** #2662: the shares' pack pictures, each share's own minute — kept here so an ended share's key goes with it. */
|
|
34
|
+
pictureLimiter = new OfficeShareLimiter();
|
|
33
35
|
constructor(deps) {
|
|
34
36
|
this.deps = deps;
|
|
35
37
|
this.shares = deps.shares ?? new OfficeShares({ changed: list => this.onChanged(list) });
|
|
@@ -37,8 +39,8 @@ export class OfficeShareService {
|
|
|
37
39
|
enabled() { return this.deps.config()?.enabled !== false; }
|
|
38
40
|
list() { return this.shares.list(); }
|
|
39
41
|
/**
|
|
40
|
-
* A new share and its URL, once the tunnel is up. `minutes` 15–max_hours×60 (60 if not given); `office` ALL_OFFICES
|
|
41
|
-
* or a stored office's id. A tunnel that cannot start ends the share at once and says why.
|
|
42
|
+
* A new share and its URL, once the tunnel is up. `minutes` 15–max_hours×60 (60 if not given); `office` ALL_OFFICES,
|
|
43
|
+
* WHOLE_FLEET (#2716) or a stored office's id; `theme` (#2715) "auto", "light" or "dark". A tunnel that cannot start ends the share at once and says why.
|
|
42
44
|
*/
|
|
43
45
|
async create(opts) {
|
|
44
46
|
if (!this.enabled())
|
|
@@ -61,7 +63,7 @@ export class OfficeShareService {
|
|
|
61
63
|
sweep() {
|
|
62
64
|
if (!this.enabled())
|
|
63
65
|
return this.shares.revoke("all");
|
|
64
|
-
return this.shares.endWhere(s => !this.deps.ownerCurrent(s.owner) || (s.office
|
|
66
|
+
return this.shares.endWhere(s => !this.deps.ownerCurrent(s.owner) || (!fleetWide(s.office) && !this.deps.officeExists(s.office)));
|
|
65
67
|
}
|
|
66
68
|
/** The fleet is stopping: every share ends and the tunnel goes. */
|
|
67
69
|
async close() {
|
|
@@ -71,8 +73,10 @@ export class OfficeShareService {
|
|
|
71
73
|
onChanged(list) {
|
|
72
74
|
const open = new Set(list.map(s => s.id));
|
|
73
75
|
for (const id of this.known)
|
|
74
|
-
if (!open.has(id))
|
|
76
|
+
if (!open.has(id)) {
|
|
75
77
|
this.limiter.forget(id);
|
|
78
|
+
this.pictureLimiter.forget(id);
|
|
79
|
+
}
|
|
76
80
|
this.known = open;
|
|
77
81
|
// While any is open, who may still have it is asked again every SWEEP_MS (an admin removed, the feature turned
|
|
78
82
|
// off, an office deleted end theirs without anyone looking).
|
|
@@ -103,8 +107,8 @@ export class OfficeShareService {
|
|
|
103
107
|
await this.stopping?.catch(() => { });
|
|
104
108
|
const abort = this.abort = new AbortController();
|
|
105
109
|
const gateway = (this.deps.createGateway ?? createOfficeShareGateway)({
|
|
106
|
-
shares: this.shares, source: this.deps.source, limiter: this.limiter, statics: officeShareStatic,
|
|
107
|
-
page:
|
|
110
|
+
shares: this.shares, source: this.deps.source, limiter: this.limiter, pictureLimiter: this.pictureLimiter, statics: officeShareStatic,
|
|
111
|
+
page: share => ({ body: officeSharePage(share.theme), csp: OFFICE_SHARE_PAGE_CSP }),
|
|
108
112
|
});
|
|
109
113
|
this.gateway = gateway;
|
|
110
114
|
const fail = (reason, detail) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"office-share-service.js","sourceRoot":"","sources":["../src/office-share-service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"office-share-service.js","sourceRoot":"","sources":["../src/office-share-service.ts"],"names":[],"mappings":"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"}
|
package/dist/office-share.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createServer, type Server } from "node:http";
|
|
2
2
|
import { type OfficeAct } from "./web-office-acts.js";
|
|
3
|
+
import { type OfficeLevel } from "./office-levels.js";
|
|
3
4
|
import type { Office } from "./web-office-store.js";
|
|
4
5
|
export declare const OFFICE_SHARE_LIMITS: {
|
|
5
6
|
/** Shares open at once, fleet-wide. */
|
|
@@ -25,20 +26,35 @@ export declare const OFFICE_SHARE_LIMITS: {
|
|
|
25
26
|
};
|
|
26
27
|
/** "All offices": every stored office (and whoever is in none); otherwise a stored office's id. */
|
|
27
28
|
export declare const ALL_OFFICES = "all";
|
|
29
|
+
/** #2716: the whole-fleet office (fleet-office-model.js AUTO_ID, web-office-store.ts AUTO_OFFICE_ID): everyone, as All offices. */
|
|
30
|
+
export declare const WHOLE_FLEET = "auto";
|
|
31
|
+
/** A share of everyone (All offices, or the whole-fleet office), not of one stored office. */
|
|
32
|
+
export declare const fleetWide: (office: string) => boolean;
|
|
33
|
+
/** #2715: how a share's page looks — the viewer's device's light or dark ("auto"), or the one its maker picked. */
|
|
34
|
+
export declare const SHARE_THEMES: readonly ["auto", "light", "dark"];
|
|
35
|
+
export type ShareTheme = (typeof SHARE_THEMES)[number];
|
|
28
36
|
/**
|
|
29
37
|
* An office's members after the share's cut, when every one it named is out of the share: a team no chart has (a team's
|
|
30
38
|
* name has no control character), so the page finds nobody — never [], which the page reads as everyone.
|
|
31
39
|
*/
|
|
32
40
|
export declare const NOBODY = "team:\0";
|
|
41
|
+
/**
|
|
42
|
+
* #2691: whom a reserved seat is for after the share's cut, when every one it named is out of the share: a name no
|
|
43
|
+
* instance has (an instance's name never starts with the members' "team:"), so the seat stays reserved for someone
|
|
44
|
+
* away — never [], which is no reservation — and still passes the page's check of an instance's name.
|
|
45
|
+
*/
|
|
46
|
+
export declare const SOMEONE_ELSE = "team:";
|
|
33
47
|
/** An ended (or unknown) share's page: the same words whichever it is. */
|
|
34
48
|
export declare const ENDED_PAGE: string;
|
|
35
49
|
export interface OfficeShare {
|
|
36
50
|
/** A short handle for lists and revoking (never the token). */
|
|
37
51
|
id: string;
|
|
38
|
-
/** ALL_OFFICES, or a stored office's id. */
|
|
52
|
+
/** ALL_OFFICES, WHOLE_FLEET (#2716), or a stored office's id. */
|
|
39
53
|
office: string;
|
|
40
54
|
/** Agents' own lines (office_set `say`) are shown. */
|
|
41
55
|
text: boolean;
|
|
56
|
+
/** #2715: the page's look. */
|
|
57
|
+
theme: ShareTheme;
|
|
42
58
|
/** Who made it (for the list and General's announcement). */
|
|
43
59
|
owner: string;
|
|
44
60
|
/** Wall-clock ms, for display; the expiry itself runs on the monotonic clock. */
|
|
@@ -51,7 +67,7 @@ export type OfficeShareCreate = {
|
|
|
51
67
|
token: string;
|
|
52
68
|
} | {
|
|
53
69
|
ok: false;
|
|
54
|
-
error: "too_many" | "duration" | "office";
|
|
70
|
+
error: "too_many" | "duration" | "office" | "theme";
|
|
55
71
|
};
|
|
56
72
|
export declare const hashShareToken: (token: string) => string;
|
|
57
73
|
/** The longest a share may live, in minutes: `web.office_share.max_hours` (whole hours, 1–24), 24 by default. */
|
|
@@ -76,11 +92,12 @@ export declare class OfficeShares {
|
|
|
76
92
|
setTimer?(fn: () => void, ms: number): ReturnType<typeof setTimeout> | null;
|
|
77
93
|
clearTimer?(t: ReturnType<typeof setTimeout>): void;
|
|
78
94
|
});
|
|
79
|
-
/** A new share: `minutes` from 15 to `maxMinutes`; `office` ALL_OFFICES or an id `exists` knows. */
|
|
95
|
+
/** A new share: `minutes` from 15 to `maxMinutes`; `office` ALL_OFFICES, WHOLE_FLEET or an id `exists` knows; `theme` (#2715) "auto" unless asked. */
|
|
80
96
|
create(opts: {
|
|
81
97
|
office: string;
|
|
82
98
|
minutes?: number;
|
|
83
99
|
text?: boolean;
|
|
100
|
+
theme?: string;
|
|
84
101
|
owner: string;
|
|
85
102
|
maxMinutes?: number;
|
|
86
103
|
exists?(office: string): boolean;
|
|
@@ -128,10 +145,11 @@ export interface OfficeShareSource {
|
|
|
128
145
|
general?: unknown;
|
|
129
146
|
teams?: unknown;
|
|
130
147
|
};
|
|
131
|
-
/** The stored offices (web-office-store.ts list()). */
|
|
148
|
+
/** The stored offices (web-office-store.ts list()); #2716: and the whole-fleet office's style, when one is kept. */
|
|
132
149
|
offices(): {
|
|
133
150
|
rev: number;
|
|
134
151
|
offices: Office[];
|
|
152
|
+
autoSkin?: string;
|
|
135
153
|
};
|
|
136
154
|
/** The live acts (OfficeActs live()). */
|
|
137
155
|
acts(): OfficeAct[];
|
|
@@ -157,6 +175,8 @@ export interface OfficeShareSource {
|
|
|
157
175
|
get(id: string): unknown;
|
|
158
176
|
picture(id: string, sha: string): Buffer | null;
|
|
159
177
|
};
|
|
178
|
+
/** #2661: the Office levels as last worked out (never waits); null: off, or none worked out yet. */
|
|
179
|
+
levels?(): Record<string, OfficeLevel> | null;
|
|
160
180
|
}
|
|
161
181
|
/**
|
|
162
182
|
* One share's picture, read once from `src`: who is in it (`scope`), and each read the page makes, cut to them.
|
|
@@ -177,27 +197,43 @@ export declare class OfficeShareView {
|
|
|
177
197
|
/**
|
|
178
198
|
* /ui/offices's answer, the shared office (or all) only, each cut to the share (#2637 review): its members only those
|
|
179
199
|
* shown (an instance, or a team with someone shown; NOBODY when none is), and a seat kept for someone not shown keeps
|
|
180
|
-
* its place but not the name
|
|
200
|
+
* its place but not the name; #2691: a seat reserved for someone not shown keeps the reservation but not the name
|
|
201
|
+
* (cutOwners). The stored offices are never changed.
|
|
181
202
|
*/
|
|
182
203
|
officeList(): {
|
|
183
204
|
rev: number;
|
|
184
205
|
offices: Office[];
|
|
185
206
|
readable: boolean;
|
|
186
207
|
};
|
|
208
|
+
/**
|
|
209
|
+
* #2661: /ui/office-levels's answer: the level of each person shown and nothing else — never the work tokens, today's
|
|
210
|
+
* counts or first seen (a share shows names and states). `enabled` false when levels are off.
|
|
211
|
+
*/
|
|
212
|
+
levels(): {
|
|
213
|
+
enabled: boolean;
|
|
214
|
+
unit: number;
|
|
215
|
+
detail: false;
|
|
216
|
+
levels: Record<string, {
|
|
217
|
+
level: number;
|
|
218
|
+
}>;
|
|
219
|
+
};
|
|
187
220
|
/** /ui/office-acts's answer: the acts of the people shown, in a shown office, without their line unless shared. */
|
|
188
221
|
acts(): {
|
|
189
222
|
acts: OfficeAct[];
|
|
190
223
|
now: number;
|
|
191
224
|
};
|
|
225
|
+
/** #2691: a seat's owners cut to the share — those shown, else (all hidden) SOMEONE_ELSE; none stays none. */
|
|
226
|
+
private cutOwners;
|
|
192
227
|
/**
|
|
193
228
|
* A pack's answer as this share gets it (#2637 review): its manifest's instance names cut to the share — `byInstance`
|
|
194
229
|
* only for people shown, a furniture item's `activeFor` only them (none left: [], which watches nobody, never the
|
|
195
230
|
* missing key, which watches everyone). #2662: and only the character sets someone shown wears (theirs in `byInstance`,
|
|
196
231
|
* and `default` for everyone else), with `files` only the pictures it still names: the page loads what is drawn, not
|
|
197
|
-
* every set of a fleet-sized pack.
|
|
232
|
+
* every set of a fleet-sized pack. #2691: a reserved seat's `for` cut as an office's (cutOwners). #2661: a title
|
|
233
|
+
* pinned (`levels.fixed`) only for people shown or an NPC. A copy: the kept pack is never changed.
|
|
198
234
|
*/
|
|
199
235
|
pack(answer: unknown): unknown;
|
|
200
|
-
/** The packs the shown offices are drawn in (a pack
|
|
236
|
+
/** The packs the shown offices are drawn in, #2716: the whole-fleet office's too (a pack none of them names is not offered). */
|
|
201
237
|
skinIds(): Set<string>;
|
|
202
238
|
}
|
|
203
239
|
/**
|
|
@@ -209,7 +245,7 @@ export declare function sharePictureRate(share: Pick<OfficeShare, "office">, off
|
|
|
209
245
|
id: string;
|
|
210
246
|
files?: number;
|
|
211
247
|
bytes?: number;
|
|
212
|
-
}
|
|
248
|
+
}>, autoSkin?: string): OfficeShareRate;
|
|
213
249
|
export interface OfficeShareGateway {
|
|
214
250
|
readonly server: Server;
|
|
215
251
|
readonly readinessMarker: string;
|
|
@@ -225,6 +261,7 @@ export interface OfficeShareGateway {
|
|
|
225
261
|
* /o/<t>/state OfficeShareView.state()
|
|
226
262
|
* /o/<t>/offices OfficeShareView.officeList()
|
|
227
263
|
* /o/<t>/office-acts OfficeShareView.acts()
|
|
264
|
+
* /o/<t>/office-levels OfficeShareView.levels() (#2661: the level only)
|
|
228
265
|
* /o/<t>/office-skins the packs the shown offices use
|
|
229
266
|
* /o/<t>/office-skins/<id> one of them, its instance names cut to the share (never cached)
|
|
230
267
|
* /o/<t>/office-skins/<id>/img/<sha>.png its picture (immutable)
|